From 7f1a4587b7fa73de15c00890a130420eb5fea3c9 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 15 Nov 2025 14:07:26 +0100 Subject: [PATCH 01/62] Generate SDK - with JSON:API support --- .gitignore | 6 + CLAUDE.md | 255 ++++++++++++++++++ README.md | 113 +++++++- composer.json | 30 +++ example.php | 60 +++++ generator/JsonApiDtoGenerator.php | 160 +++++++++++ generator/generate.php | 104 +++++++ regenerate-sdk.sh | 20 ++ src/Attributes/DateTime.php | 16 ++ src/Attributes/Property.php | 16 ++ src/Dto/Approve.php | 39 +++ src/Dto/Attributes.php | 15 ++ src/Dto/Budget.php | 62 +++++ src/Dto/BudgetTimeSpentTotal.php | 27 ++ src/Dto/BudgetType.php | 36 +++ src/Dto/Correction.php | 18 ++ src/Dto/Customer.php | 30 +++ src/Dto/DailyProgress.php | 20 ++ src/Dto/Entry.php | 90 +++++++ src/Dto/EntrySuggestion.php | 39 +++ src/Dto/Event.php | 54 ++++ src/Dto/ExportMail.php | 10 + src/Dto/MarkAsExported.php | 39 +++ src/Dto/MarkAsInvoiced.php | 90 +++++++ src/Dto/Overtime.php | 39 +++ src/Dto/Team.php | 24 ++ src/Dto/TimeSpentTotal.php | 30 +++ src/Dto/User.php | 15 ++ src/Dto/UserCustomerHoursAggregate.php | 24 ++ src/Foundation/HasAttributes.php | 66 +++++ src/Foundation/Model.php | 67 +++++ src/Foundation/ModelInterface.php | 14 + src/Requests/Approve/PostOvertimeApprove.php | 34 +++ src/Requests/Budget/DeleteBudget.php | 30 +++ src/Requests/Budget/GetBudget.php | 30 +++ src/Requests/Budget/GetBudgets.php | 95 +++++++ src/Requests/Budget/PatchBudget.php | 34 +++ src/Requests/Budget/PostBudgets.php | 30 +++ src/Requests/Budget/PutBudget.php | 30 +++ .../GetBudgetTimeSpentTotals.php | 38 +++ src/Requests/BudgetType/GetBudgetTypes.php | 26 ++ src/Requests/Change/GetChange.php | 30 +++ src/Requests/Change/GetChanges.php | 26 ++ src/Requests/Correction/PatchCorrection.php | 34 +++ src/Requests/Correction/PostCorrections.php | 30 +++ src/Requests/Correction/PutCorrection.php | 30 +++ src/Requests/Customer/DeleteCustomer.php | 30 +++ src/Requests/Customer/GetCustomer.php | 30 +++ src/Requests/Customer/GetCustomers.php | 38 +++ src/Requests/Customer/PatchCustomer.php | 34 +++ src/Requests/Customer/PostCustomers.php | 30 +++ src/Requests/Customer/PutCustomer.php | 30 +++ .../DailyProgress/GetDailyProgresses.php | 26 ++ .../EntriesExport/GetBudgetEntriesExport.php | 241 +++++++++++++++++ src/Requests/Entry/DeleteEntry.php | 30 +++ src/Requests/Entry/GetEntries.php | 239 ++++++++++++++++ src/Requests/Entry/GetEntry.php | 30 +++ src/Requests/Entry/PatchEntry.php | 34 +++ src/Requests/Entry/PostEntries.php | 30 +++ src/Requests/Entry/PutEntry.php | 30 +++ .../EntrySuggestion/DeleteEntrySuggestion.php | 30 +++ .../EntrySuggestion/GetEntrySuggestion.php | 30 +++ .../EntrySuggestion/GetEntrySuggestions.php | 59 ++++ src/Requests/Event/PostEvents.php | 30 +++ .../ExportMail/GetBudgetsExportMails.php | 26 ++ src/Requests/Incident/GetIncident.php | 30 +++ src/Requests/Incident/GetIncidents.php | 26 ++ .../PostOvertimeMarkAsExported.php | 34 +++ .../PostEntryMarkAsInvoiced.php | 34 +++ src/Requests/Me/GetMes.php | 26 ++ src/Requests/Number/GetIncidentsNumber.php | 30 +++ src/Requests/Overtime/GetOvertimes.php | 113 ++++++++ src/Requests/Period/GetBudgetPeriods.php | 30 +++ src/Requests/Team/DeleteTeam.php | 30 +++ src/Requests/Team/GetTeam.php | 30 +++ src/Requests/Team/GetTeams.php | 26 ++ src/Requests/Team/PatchTeam.php | 34 +++ src/Requests/Team/PostTeams.php | 30 +++ src/Requests/Team/PutTeam.php | 30 +++ .../TimeSpentTotal/GetTimeSpentTotals.php | 53 ++++ src/Requests/User/DeleteUser.php | 30 +++ src/Requests/User/GetUser.php | 30 +++ src/Requests/User/GetUsers.php | 38 +++ src/Requests/User/PatchUser.php | 34 +++ src/Requests/User/PostUsers.php | 30 +++ src/Requests/User/PutUser.php | 30 +++ .../GetUserCustomerHoursAggregates.php | 131 +++++++++ src/Resource/Approve.php | 18 ++ src/Resource/Budget.php | 105 ++++++++ src/Resource/BudgetTimeSpentTotal.php | 19 ++ src/Resource/BudgetType.php | 15 ++ src/Resource/Change.php | 25 ++ src/Resource/Correction.php | 35 +++ src/Resource/Customer.php | 66 +++++ src/Resource/DailyProgress.php | 15 ++ src/Resource/EntriesExport.php | 156 +++++++++++ src/Resource/Entry.php | 201 ++++++++++++++ src/Resource/EntrySuggestion.php | 54 ++++ src/Resource/Event.php | 15 ++ src/Resource/ExportMail.php | 15 ++ src/Resource/Incident.php | 25 ++ src/Resource/MarkAsExported.php | 18 ++ src/Resource/MarkAsInvoiced.php | 18 ++ src/Resource/Me.php | 15 ++ src/Resource/Number.php | 18 ++ src/Resource/Overtime.php | 70 +++++ src/Resource/Period.php | 18 ++ src/Resource/Team.php | 62 +++++ src/Resource/TimeSpentTotal.php | 30 +++ src/Resource/User.php | 66 +++++ src/Resource/UserCustomerHoursAggregate.php | 82 ++++++ src/Timatic.php | 189 +++++++++++++ 112 files changed, 5380 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 CLAUDE.md create mode 100644 composer.json create mode 100644 example.php create mode 100644 generator/JsonApiDtoGenerator.php create mode 100644 generator/generate.php create mode 100755 regenerate-sdk.sh create mode 100644 src/Attributes/DateTime.php create mode 100644 src/Attributes/Property.php create mode 100644 src/Dto/Approve.php create mode 100644 src/Dto/Attributes.php create mode 100644 src/Dto/Budget.php create mode 100644 src/Dto/BudgetTimeSpentTotal.php create mode 100644 src/Dto/BudgetType.php create mode 100644 src/Dto/Correction.php create mode 100644 src/Dto/Customer.php create mode 100644 src/Dto/DailyProgress.php create mode 100644 src/Dto/Entry.php create mode 100644 src/Dto/EntrySuggestion.php create mode 100644 src/Dto/Event.php create mode 100644 src/Dto/ExportMail.php create mode 100644 src/Dto/MarkAsExported.php create mode 100644 src/Dto/MarkAsInvoiced.php create mode 100644 src/Dto/Overtime.php create mode 100644 src/Dto/Team.php create mode 100644 src/Dto/TimeSpentTotal.php create mode 100644 src/Dto/User.php create mode 100644 src/Dto/UserCustomerHoursAggregate.php create mode 100644 src/Foundation/HasAttributes.php create mode 100644 src/Foundation/Model.php create mode 100644 src/Foundation/ModelInterface.php create mode 100644 src/Requests/Approve/PostOvertimeApprove.php create mode 100644 src/Requests/Budget/DeleteBudget.php create mode 100644 src/Requests/Budget/GetBudget.php create mode 100644 src/Requests/Budget/GetBudgets.php create mode 100644 src/Requests/Budget/PatchBudget.php create mode 100644 src/Requests/Budget/PostBudgets.php create mode 100644 src/Requests/Budget/PutBudget.php create mode 100644 src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php create mode 100644 src/Requests/BudgetType/GetBudgetTypes.php create mode 100644 src/Requests/Change/GetChange.php create mode 100644 src/Requests/Change/GetChanges.php create mode 100644 src/Requests/Correction/PatchCorrection.php create mode 100644 src/Requests/Correction/PostCorrections.php create mode 100644 src/Requests/Correction/PutCorrection.php create mode 100644 src/Requests/Customer/DeleteCustomer.php create mode 100644 src/Requests/Customer/GetCustomer.php create mode 100644 src/Requests/Customer/GetCustomers.php create mode 100644 src/Requests/Customer/PatchCustomer.php create mode 100644 src/Requests/Customer/PostCustomers.php create mode 100644 src/Requests/Customer/PutCustomer.php create mode 100644 src/Requests/DailyProgress/GetDailyProgresses.php create mode 100644 src/Requests/EntriesExport/GetBudgetEntriesExport.php create mode 100644 src/Requests/Entry/DeleteEntry.php create mode 100644 src/Requests/Entry/GetEntries.php create mode 100644 src/Requests/Entry/GetEntry.php create mode 100644 src/Requests/Entry/PatchEntry.php create mode 100644 src/Requests/Entry/PostEntries.php create mode 100644 src/Requests/Entry/PutEntry.php create mode 100644 src/Requests/EntrySuggestion/DeleteEntrySuggestion.php create mode 100644 src/Requests/EntrySuggestion/GetEntrySuggestion.php create mode 100644 src/Requests/EntrySuggestion/GetEntrySuggestions.php create mode 100644 src/Requests/Event/PostEvents.php create mode 100644 src/Requests/ExportMail/GetBudgetsExportMails.php create mode 100644 src/Requests/Incident/GetIncident.php create mode 100644 src/Requests/Incident/GetIncidents.php create mode 100644 src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php create mode 100644 src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php create mode 100644 src/Requests/Me/GetMes.php create mode 100644 src/Requests/Number/GetIncidentsNumber.php create mode 100644 src/Requests/Overtime/GetOvertimes.php create mode 100644 src/Requests/Period/GetBudgetPeriods.php create mode 100644 src/Requests/Team/DeleteTeam.php create mode 100644 src/Requests/Team/GetTeam.php create mode 100644 src/Requests/Team/GetTeams.php create mode 100644 src/Requests/Team/PatchTeam.php create mode 100644 src/Requests/Team/PostTeams.php create mode 100644 src/Requests/Team/PutTeam.php create mode 100644 src/Requests/TimeSpentTotal/GetTimeSpentTotals.php create mode 100644 src/Requests/User/DeleteUser.php create mode 100644 src/Requests/User/GetUser.php create mode 100644 src/Requests/User/GetUsers.php create mode 100644 src/Requests/User/PatchUser.php create mode 100644 src/Requests/User/PostUsers.php create mode 100644 src/Requests/User/PutUser.php create mode 100644 src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php create mode 100644 src/Resource/Approve.php create mode 100644 src/Resource/Budget.php create mode 100644 src/Resource/BudgetTimeSpentTotal.php create mode 100644 src/Resource/BudgetType.php create mode 100644 src/Resource/Change.php create mode 100644 src/Resource/Correction.php create mode 100644 src/Resource/Customer.php create mode 100644 src/Resource/DailyProgress.php create mode 100644 src/Resource/EntriesExport.php create mode 100644 src/Resource/Entry.php create mode 100644 src/Resource/EntrySuggestion.php create mode 100644 src/Resource/Event.php create mode 100644 src/Resource/ExportMail.php create mode 100644 src/Resource/Incident.php create mode 100644 src/Resource/MarkAsExported.php create mode 100644 src/Resource/MarkAsInvoiced.php create mode 100644 src/Resource/Me.php create mode 100644 src/Resource/Number.php create mode 100644 src/Resource/Overtime.php create mode 100644 src/Resource/Period.php create mode 100644 src/Resource/Team.php create mode 100644 src/Resource/TimeSpentTotal.php create mode 100644 src/Resource/User.php create mode 100644 src/Resource/UserCustomerHoursAggregate.php create mode 100644 src/Timatic.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d36fd87 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/vendor/ +/.idea/ +composer.lock +.phpunit.result.cache +.DS_Store +openapi.json diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..099a2d2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,255 @@ + +=== foundation rules === + +# Laravel Boost Guidelines + +The Laravel Boost guidelines are specifically curated by Laravel maintainers for this application. These guidelines should be followed closely to enhance the user's satisfaction building Laravel applications. + +## Foundational Context +This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. + +- php - 8.4.14 +- laravel/framework (LARAVEL) - v11 +- laravel/horizon (HORIZON) - v5 +- laravel/pint (PINT) - v1 +- laravel/prompts (PROMPTS) - v0 +- laravel/sanctum (SANCTUM) - v4 +- laravel/breeze (BREEZE) - v2 +- laravel/mcp (MCP) - v0 +- laravel/sail (SAIL) - v1 +- phpunit/phpunit (PHPUNIT) - v11 +- vue (VUE) - v3 +- prettier (PRETTIER) - v3 +- tailwindcss (TAILWINDCSS) - v3 + + +## Conventions +- You must follow all existing code conventions used in this application. When creating or editing a file, check sibling files for the correct structure, approach, naming. +- Use descriptive names for variables and methods. For example, `isRegisteredForDiscounts`, not `discount()`. +- Check for existing components to reuse before writing a new one. + +## Verification Scripts +- Do not create verification scripts or tinker when tests cover that functionality and prove it works. Unit and feature tests are more important. + +## Application Structure & Architecture +- Stick to existing directory structure - don't create new base folders without approval. +- Do not change the application's dependencies without approval. + +## Frontend Bundling +- If the user doesn't see a frontend change reflected in the UI, it could mean they need to run `npm run build`, `npm run dev`, or `composer run dev`. Ask them. + +## Replies +- Be concise in your explanations - focus on what's important rather than explaining obvious details. + +## Documentation Files +- You must only create documentation files if explicitly requested by the user. + + +=== boost rules === + +## Laravel Boost +- Laravel Boost is an MCP server that comes with powerful tools designed specifically for this application. Use them. + +## Artisan +- Use the `list-artisan-commands` tool when you need to call an Artisan command to double check the available parameters. + +## URLs +- Whenever you share a project URL with the user you should use the `get-absolute-url` tool to ensure you're using the correct scheme, domain / IP, and port. + +## Tinker / Debugging +- You should use the `tinker` tool when you need to execute PHP to debug code or query Eloquent models directly. +- Use the `database-query` tool when you only need to read from the database. + +## Reading Browser Logs With the `browser-logs` Tool +- You can read browser logs, errors, and exceptions using the `browser-logs` tool from Boost. +- Only recent browser logs will be useful - ignore old logs. + +## Searching Documentation (Critically Important) +- Boost comes with a powerful `search-docs` tool you should use before any other approaches. This tool automatically passes a list of installed packages and their versions to the remote Boost API, so it returns only version-specific documentation specific for the user's circumstance. You should pass an array of packages to filter on if you know you need docs for particular packages. +- The 'search-docs' tool is perfect for all Laravel related packages, including Laravel, Inertia, Livewire, Filament, Tailwind, Pest, Nova, Nightwatch, etc. +- You must use this tool to search for Laravel-ecosystem documentation before falling back to other approaches. +- Search the documentation before making code changes to ensure we are taking the correct approach. +- Use multiple, broad, simple, topic based queries to start. For example: `['rate limiting', 'routing rate limiting', 'routing']`. +- Do not add package names to queries - package information is already shared. For example, use `test resource table`, not `filament 4 test resource table`. + +### Available Search Syntax +- You can and should pass multiple queries at once. The most relevant results will be returned first. + +1. Simple Word Searches with auto-stemming - query=authentication - finds 'authenticate' and 'auth' +2. Multiple Words (AND Logic) - query=rate limit - finds knowledge containing both "rate" AND "limit" +3. Quoted Phrases (Exact Position) - query="infinite scroll" - Words must be adjacent and in that order +4. Mixed Queries - query=middleware "rate limit" - "middleware" AND exact phrase "rate limit" +5. Multiple Queries - queries=["authentication", "middleware"] - ANY of these terms + + +=== php rules === + +## PHP + +- Always use curly braces for control structures, even if it has one line. + +### Constructors +- Use PHP 8 constructor property promotion in `__construct()`. + - public function __construct(public GitHub $github) { } +- Do not allow empty `__construct()` methods with zero parameters. + +### Type Declarations +- Always use explicit return type declarations for methods and functions. +- Use appropriate PHP type hints for method parameters. + + +protected function isAccessible(User $user, ?string $path = null): bool +{ + ... +} + + +## Comments +- Prefer PHPDoc blocks over comments. Never use comments within the code itself unless there is something _very_ complex going on. + +## PHPDoc Blocks +- Add useful array shape type definitions for arrays when appropriate. + +## Enums +- Typically, keys in an Enum should be TitleCase. For example: `FavoritePerson`, `BestLake`, `Monthly`. + + +=== herd rules === + +## Laravel Herd + +- The application is served by Laravel Herd and will be available at: https?://[kebab-case-project-dir].test. Use the `get-absolute-url` tool to generate URLs for the user to ensure valid URLs. +- You must not run any commands to make the site available via HTTP(s). It is _always_ available through Laravel Herd. + + +=== laravel/core rules === + +## Do Things the Laravel Way + +- Use `php artisan make:` commands to create new files (i.e. migrations, controllers, models, etc.). You can list available Artisan commands using the `list-artisan-commands` tool. +- If you're creating a generic PHP class, use `artisan make:class`. +- Pass `--no-interaction` to all Artisan commands to ensure they work without user input. You should also pass the correct `--options` to ensure correct behavior. + +### Database +- Always use proper Eloquent relationship methods with return type hints. Prefer relationship methods over raw queries or manual joins. +- Use Eloquent models and relationships before suggesting raw database queries +- Avoid `DB::`; prefer `Model::query()`. Generate code that leverages Laravel's ORM capabilities rather than bypassing them. +- Generate code that prevents N+1 query problems by using eager loading. +- Use Laravel's query builder for very complex database operations. + +### Model Creation +- When creating new models, create useful factories and seeders for them too. Ask the user if they need any other things, using `list-artisan-commands` to check the available options to `php artisan make:model`. + +### APIs & Eloquent Resources +- For APIs, default to using Eloquent API Resources and API versioning unless existing API routes do not, then you should follow existing application convention. + +### Controllers & Validation +- Always create Form Request classes for validation rather than inline validation in controllers. Include both validation rules and custom error messages. +- Check sibling Form Requests to see if the application uses array or string based validation rules. + +### Queues +- Use queued jobs for time-consuming operations with the `ShouldQueue` interface. + +### Authentication & Authorization +- Use Laravel's built-in authentication and authorization features (gates, policies, Sanctum, etc.). + +### URL Generation +- When generating links to other pages, prefer named routes and the `route()` function. + +### Configuration +- Use environment variables only in configuration files - never use the `env()` function directly outside of config files. Always use `config('app.name')`, not `env('APP_NAME')`. + +### Testing +- When creating models for tests, use the factories for the models. Check if the factory has custom states that can be used before manually setting up the model. +- Faker: Use methods such as `$this->faker->word()` or `fake()->randomDigit()`. Follow existing conventions whether to use `$this->faker` or `fake()`. +- When creating tests, make use of `php artisan make:test [options] ` to create a feature test, and pass `--unit` to create a unit test. Most tests should be feature tests. + +### Vite Error +- If you receive an "Illuminate\Foundation\ViteException: Unable to locate file in Vite manifest" error, you can run `npm run build` or ask the user to run `npm run dev` or `composer run dev`. + + +=== laravel/v11 rules === + +## Laravel 11 + +- Use the `search-docs` tool to get version specific documentation. +- Laravel 11 brought a new streamlined file structure which this project now uses. + +### Laravel 11 Structure +- No middleware files in `app/Http/Middleware/`. +- `bootstrap/app.php` is the file to register middleware, exceptions, and routing files. +- `bootstrap/providers.php` contains application specific service providers. +- **No app\Console\Kernel.php** - use `bootstrap/app.php` or `routes/console.php` for console configuration. +- **Commands auto-register** - files in `app/Console/Commands/` are automatically available and do not require manual registration. + +### Database +- When modifying a column, the migration must include all of the attributes that were previously defined on the column. Otherwise, they will be dropped and lost. +- Laravel 11 allows limiting eagerly loaded records natively, without external packages: `$query->latest()->limit(10);`. + +### Models +- Casts can and likely should be set in a `casts()` method on a model rather than the `$casts` property. Follow existing conventions from other models. + +### New Artisan Commands +- List Artisan commands using Boost's MCP tool, if available. New commands available in Laravel 11: + - `php artisan make:enum` + - `php artisan make:class` + - `php artisan make:interface` + + +=== pint/core rules === + +## Laravel Pint Code Formatter + +- You must run `vendor/bin/pint --dirty` before finalizing changes to ensure your code matches the project's expected style. +- Do not run `vendor/bin/pint --test`, simply run `vendor/bin/pint` to fix any formatting issues. + + +=== phpunit/core rules === + +## PHPUnit Core + +- This application uses PHPUnit for testing. All tests must be written as PHPUnit classes. Use `php artisan make:test --phpunit ` to create a new test. +- If you see a test using "Pest", convert it to PHPUnit. +- Every time a test has been updated, run that singular test. +- When the tests relating to your feature are passing, ask the user if they would like to also run the entire test suite to make sure everything is still passing. +- Tests should test all of the happy paths, failure paths, and weird paths. +- You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files, these are core to the application. + +### Running Tests +- Run the minimal number of tests, using an appropriate filter, before finalizing. +- To run all tests: `php artisan test`. +- To run all tests in a file: `php artisan test tests/Feature/ExampleTest.php`. +- To filter on a particular test name: `php artisan test --filter=testName` (recommended after making a change to a related file). + + +=== tailwindcss/core rules === + +## Tailwind Core + +- Use Tailwind CSS classes to style HTML, check and use existing tailwind conventions within the project before writing your own. +- Offer to extract repeated patterns into components that match the project's conventions (i.e. Blade, JSX, Vue, etc..) +- Think through class placement, order, priority, and defaults - remove redundant classes, add classes to parent or child carefully to limit repetition, group elements logically +- You can use the `search-docs` tool to get exact examples from the official documentation when needed. + +### Spacing +- When listing items, use gap utilities for spacing, don't use margins. + + +
+
Superior
+
Michigan
+
Erie
+
+
+ + +### Dark Mode +- If existing pages and components support dark mode, new pages and components must support dark mode in a similar way, typically using `dark:`. + + +=== tailwindcss/v3 rules === + +## Tailwind 3 + +- Always use Tailwind CSS v3 - verify you're using only classes supported by this version. +
\ No newline at end of file diff --git a/README.md b/README.md index c4e3755..0907698 100644 --- a/README.md +++ b/README.md @@ -1 +1,112 @@ -# timatic-php-sdk +# Timatic PHP SDK + +A PHP SDK for the Timatic API, built with [Saloon](https://docs.saloon.dev/) and automatically generated from OpenAPI specifications. + +## Installation + +```bash +composer require timatic/php-sdk +``` + +## Usage + +```php +use Timatic\SDK\Timatic; + +// Initialize the SDK +$timatic = new Timatic(); + +// Example: Get users +$response = $timatic->user()->getUsers(); +$users = $response->json(); + +// Example: Get a specific customer +$response = $timatic->customer()->getCustomer(id: 1); +$customer = $response->json(); + +// Example: Create a new budget +$response = $timatic->budget()->postBudgets([ + 'name' => 'Q1 2024 Budget', + // ... other budget data +]); +``` + +## Available Resources + +The SDK provides access to the following resources: + +- **Budgets** - Manage budgets and budget entries +- **Customers** - Customer management +- **Users** - User management +- **Teams** - Team management +- **Entries** - Time entry management +- **Incidents** - Incident tracking +- **Changes** - Change tracking +- **Overtimes** - Overtime management +- **Events** - Event logging +- And more... + +## JSON:API Support + +This SDK uses a custom **JSON:API DTO Generator** that automatically flattens JSON:API attributes into proper Model properties. Instead of having generic `$attributes`, `$type`, and `$relationships` objects, each model has specific typed properties. + +### Example + +Instead of: +```php +$budget->attributes->title; // āŒ Generic structure +``` + +You get: +```php +$budget->title; // āœ… Proper typed property +$budget->budgetTypeId; +$budget->startedAt; // Carbon instance for datetime fields +``` + +### Model Features + +- **Extends `Model` base class** with JSON:API support +- **Property attributes** via `#[Property]` for serialization +- **DateTime handling** with Carbon instances +- **Type safety** with PHP 8.1+ type hints +- **HasAttributes trait** for easy attribute manipulation + +## Regenerating the SDK + +This SDK is automatically generated from the Timatic API OpenAPI specification using a custom JSON:API generator. To regenerate the SDK with the latest API changes: + +```bash +./regenerate-sdk.sh +``` + +This will: +1. Download the latest OpenAPI specification from the API +2. Generate Models with flattened JSON:API attributes +3. Update the autoloader + +### How It Works + +The SDK uses a custom `JsonApiDtoGenerator` that: +1. Detects JSON:API schemas in the OpenAPI specification +2. Extracts properties from the `attributes` object +3. Generates proper Model classes with specific properties +4. Adds `#[Property]` and `#[DateTime]` attributes +5. Uses Carbon for datetime fields + +## Development + +### Running Tests + +```bash +composer test +``` + +## License + +This package is licensed under the Elastic License 2.0 (ELv2). + +## Credits + +- Built with [Saloon](https://docs.saloon.dev/) +- Generated using [Saloon SDK Generator](https://docs.saloon.dev/installable-plugins/sdk-generator) diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..346289a --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "name": "timatic/php-sdk", + "description": "PHP SDK for Timatic API built with Saloon", + "type": "library", + "license": "Elastic-2.0", + "autoload": { + "psr-4": { + "Timatic\\SDK\\": "src/", + "Timatic\\SDK\\Generator\\": "generator/" + } + }, + "autoload-dev": { + "psr-4": { + "Timatic\\SDK\\Tests\\": "tests/" + } + }, + "require": { + "php": "^8.1", + "saloonphp/saloon": "^3.0", + "nesbot/carbon": "^2.0|^3.0", + "illuminate/support": "^10.0|^11.0" + }, + "require-dev": { + "laravel/boost": "^1.8", + "phpunit/phpunit": "^10.0|^11.0", + "crescat-io/saloon-sdk-generator": "^1.0" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/example.php b/example.php new file mode 100644 index 0000000..2655e60 --- /dev/null +++ b/example.php @@ -0,0 +1,60 @@ +user()->getUsers(); +if ($response->successful()) { + $users = $response->json(); + echo "Found " . count($users['data'] ?? []) . " users\n\n"; +} + +// Example 2: Get all customers +echo "Fetching customers...\n"; +$response = $timatic->customer()->getCustomers(); +if ($response->successful()) { + $customers = $response->json(); + echo "Found " . count($customers['data'] ?? []) . " customers\n\n"; +} + +// Example 3: Get budgets +echo "Fetching budgets...\n"; +$response = $timatic->budget()->getBudgets(); +if ($response->successful()) { + $budgets = $response->json(); + echo "Found " . count($budgets['data'] ?? []) . " budgets\n\n"; +} + +// Example 4: Get current user info +echo "Fetching current user info...\n"; +$response = $timatic->me()->getMes(); +if ($response->successful()) { + $me = $response->json(); + echo "Current user: " . ($me['data']['name'] ?? 'Unknown') . "\n\n"; +} + +// Example 5: Create a new entry (commented out to prevent accidental execution) +/* +echo "Creating a new entry...\n"; +$response = $timatic->entry()->postEntries([ + 'user_id' => 1, + 'customer_id' => 1, + 'date' => date('Y-m-d'), + 'hours' => 2.5, + 'description' => 'Working on SDK integration', +]); + +if ($response->successful()) { + echo "Entry created successfully!\n"; + $entry = $response->json(); + var_dump($entry); +} +*/ + +echo "Examples completed!\n"; diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php new file mode 100644 index 0000000..2171342 --- /dev/null +++ b/generator/JsonApiDtoGenerator.php @@ -0,0 +1,160 @@ +components) { + foreach ($specification->components->schemas as $className => $schema) { + $this->generateModelClass(NameHelper::safeClassName($className), $schema); + } + } + + return $this->generated; + } + + protected function generateModelClass(string $className, Schema $schema): PhpFile + { + $modelName = NameHelper::dtoClassName($className ?: $this->config->fallbackResourceName); + + $classType = new ClassType($modelName); + $classFile = new PhpFile(); + $namespace = $classFile + ->addNamespace("{$this->config->namespace}\\{$this->config->dtoNamespaceSuffix}"); + + // Extend Model instead of Spatie Data + $classType->setExtends(Model::class) + ->setComment($schema->title ?? '') + ->addComment('') + ->addComment(Utils::wrapLongLines($schema->description ?? '')); + + // Extract properties from JSON:API structure + $properties = $this->extractJsonApiProperties($schema); + + // Add properties to the class + foreach ($properties as $propertyName => $propertySpec) { + $this->addPropertyToClass($classType, $namespace, $propertyName, $propertySpec); + } + + // Add imports + $namespace->addUse(Model::class); + $namespace->addUse(Property::class); + + $namespace->add($classType); + + $this->generated[$modelName] = $classFile; + + return $classFile; + } + + /** + * Extract properties from JSON:API schema structure + * + * @return Schema[] + */ + protected function extractJsonApiProperties(Schema $schema): array + { + // Check if this is a JSON:API schema with attributes at root level + if (isset($schema->properties['attributes'])) { + $attributesSchema = $schema->properties['attributes']; + + if ($attributesSchema instanceof Schema && isset($attributesSchema->properties)) { + // Return the flattened attributes properties + return $attributesSchema->properties; + } + } + + // Fallback to regular properties if not JSON:API structure + return $schema->properties ?? []; + } + + protected function addPropertyToClass( + ClassType $classType, + $namespace, + string $propertyName, + Schema|Reference $propertySpec + ): void { + $type = $this->convertOpenApiTypeToPhp($propertySpec); + $name = NameHelper::safeVariableName($propertyName); + + // Create public property with #[Property] attribute + $property = $classType->addProperty($name) + ->setPublic() + ->setType($type) + ->setNullable(true); + + // Add #[Property] attribute + $property->addAttribute(Property::class); + + // Check if this is a datetime field and add #[DateTime] attribute + if ($propertySpec instanceof Schema && $propertySpec->format === 'date-time') { + $property->addAttribute(DateTime::class); + $namespace->addUse(DateTime::class); + + // Change type to Carbon if datetime + if (!str_contains($type, 'Carbon')) { + $property->setType('null|\\Carbon\\Carbon'); + } + } + + // Add comment with description if available + if ($propertySpec instanceof Schema && $propertySpec->description) { + $property->addComment($propertySpec->description); + } + } + + protected function convertOpenApiTypeToPhp(Schema|Reference $schema): string + { + if ($schema instanceof Reference) { + return Str::afterLast($schema->getReference(), '/'); + } + + if (is_array($schema->type)) { + return collect($schema->type)->map(fn ($type) => $this->mapType($type))->implode('|'); + } + + if (is_string($schema->type)) { + return $this->mapType($schema->type, $schema->format); + } + + return 'mixed'; + } + + protected function mapType(string $type, ?string $format = null): string + { + return match ($type) { + 'integer' => 'int', + 'string' => 'string', + 'boolean' => 'bool', + 'object' => 'object', + 'number' => match ($format) { + 'float' => 'float', + 'int32', 'int64' => 'int', + default => 'int|float', + }, + 'array' => 'array', + 'null' => 'null', + default => 'mixed', + }; + } +} diff --git a/generator/generate.php b/generator/generate.php new file mode 100644 index 0000000..89e749f --- /dev/null +++ b/generator/generate.php @@ -0,0 +1,104 @@ + [ + 'verify_peer' => false, + 'verify_peer_name' => false, + ], +])); + +if (!$openApiJson) { + echo "āŒ Failed to download OpenAPI specification\n"; + exit(1); +} + +file_put_contents(__DIR__ . '/../openapi.json', $openApiJson); +echo "āœ… OpenAPI specification downloaded\n\n"; + +// Parse the specification +echo "šŸ”Ø Parsing OpenAPI specification...\n"; +$specification = Factory::parse('openapi', __DIR__ . '/../openapi.json'); +echo "āœ… Specification parsed\n\n"; + +// Create config +$config = new Config( + connectorName: 'Timatic', + namespace: 'Timatic\\SDK', + resourceNamespaceSuffix: 'Resource', + requestNamespaceSuffix: 'Requests', + dtoNamespaceSuffix: 'Dto', +); + +// Create code generator with our custom JSON:API DTO generator +echo "šŸ—ļø Generating SDK with JSON:API models...\n"; +$generator = new CodeGenerator( + config: $config, + dtoGenerator: new JsonApiDtoGenerator($config) +); + +// Generate the code +$result = $generator->run($specification); + +// Output directory +$outputDir = __DIR__ . '/../src'; + +// Helper function to write files +function writeFile($file, $outputDir, $namespace) { + $relativePath = str_replace($namespace, '', array_values($file->getNamespaces())[0]->getName()); + $className = array_values($file->getClasses())[0]->getName(); + $filePath = $outputDir . str_replace('\\', '/', $relativePath) . '/' . $className . '.php'; + + // Create directory if it doesn't exist + $dir = dirname($filePath); + if (!is_dir($dir)) { + mkdir($dir, 0755, true); + } + + file_put_contents($filePath, (string) $file); + + return $filePath; +} + +// Write connector +if ($result->connectorClass) { + $path = writeFile($result->connectorClass, $outputDir, $config->namespace); + echo "āœ“ Connector: {$path}\n"; +} + +// Write resources +echo "\nšŸ“¦ Resources:\n"; +foreach ($result->resourceClasses as $resourceClass) { + $path = writeFile($resourceClass, $outputDir, $config->namespace); + echo " āœ“ " . basename($path) . "\n"; +} + +// Write requests +echo "\nšŸ“ Requests:\n"; +foreach ($result->requestClasses as $requestClass) { + $path = writeFile($requestClass, $outputDir, $config->namespace); + echo " āœ“ " . basename(dirname($path)) . '/' . basename($path) . "\n"; +} + +// Write DTOs (now Models!) +echo "\nšŸŽÆ Models:\n"; +foreach ($result->dtoClasses as $dtoClass) { + $path = writeFile($dtoClass, $outputDir, $config->namespace); + echo " āœ“ " . basename($path) . "\n"; +} + +echo "\nāœ… SDK generation complete!\n"; +echo "\nšŸ’” Next steps:\n"; +echo " 1. Run 'composer dump-autoload'\n"; +echo " 2. Review generated models in src/Dto/\n"; +echo " 3. Test the SDK\n"; diff --git a/regenerate-sdk.sh b/regenerate-sdk.sh new file mode 100755 index 0000000..005d75a --- /dev/null +++ b/regenerate-sdk.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Timatic PHP SDK Regeneration Script +# This script downloads the latest OpenAPI specification and regenerates the SDK + +set -e + +echo "šŸ”„ Regenerating Timatic PHP SDK with JSON:API support..." +echo "" + +# Run the custom generator +php generator/generate.php + +# Update autoloader +echo "" +echo "šŸ“¦ Updating autoloader..." +composer dump-autoload + +echo "" +echo "āœ… All done! SDK has been regenerated with JSON:API models." diff --git a/src/Attributes/DateTime.php b/src/Attributes/DateTime.php new file mode 100644 index 0000000..0ff908c --- /dev/null +++ b/src/Attributes/DateTime.php @@ -0,0 +1,16 @@ + + */ + public function attributes(): array + { + $reflectionClass = new ReflectionClass($this); + $properties = $reflectionClass->getProperties(); + $attributes = []; + + foreach ($properties as $property) { + $propertyAttributes = $property->getAttributes(Property::class); + + if (count($propertyAttributes) > 0) { + $propertyName = $property->getName(); + + // Skip if property is not initialized + if (!$property->isInitialized($this)) { + continue; + } + + $value = $property->getValue($this); + + /** @var Property $attr */ + $attr = $propertyAttributes[0]->newInstance(); + + // Skip read-only properties when serializing (like id) + if ($attr->isReadOnly) { + continue; + } + + $attributes[$propertyName] = $value; + } + } + + return $attributes; + } + + /** + * Get a specific attribute value + */ + public function getAttribute(string $key): mixed + { + return $this->$key ?? null; + } + + /** + * Set a specific attribute value + */ + public function setAttribute(string $key, mixed $value): void + { + $this->$key = $value; + } +} diff --git a/src/Foundation/Model.php b/src/Foundation/Model.php new file mode 100644 index 0000000..7cefa46 --- /dev/null +++ b/src/Foundation/Model.php @@ -0,0 +1,67 @@ + $attributes + */ + public function __construct(array $attributes = []) + { + $this->fill($attributes); + } + + /** + * @param array $attributes + */ + public function fill(array $attributes): void + { + $propertyNames = $this->propertyNames(); + + foreach ($attributes as $key => $value) { + if (in_array($key, $propertyNames)) { + $this->$key = $value; + } + } + } + + /** + * @return array + */ + private function propertyNames(): array + { + $reflectionClass = new ReflectionClass($this); + $properties = $reflectionClass->getProperties(); + $propertyNames = []; + + foreach ($properties as $property) { + $attributes = $property->getAttributes(Property::class); + if (count($attributes) > 0) { + $propertyNames[] = $property->getName(); + } + } + + return $propertyNames; + } + + public function type(): string + { + return $this->type ?? Str::of( + (new ReflectionClass($this))->getShortName() + )->camel()->plural()->toString(); + } +} diff --git a/src/Foundation/ModelInterface.php b/src/Foundation/ModelInterface.php new file mode 100644 index 0000000..15f6db6 --- /dev/null +++ b/src/Foundation/ModelInterface.php @@ -0,0 +1,14 @@ +overtime}/approve"; + } + + + /** + * @param string $overtime + */ + public function __construct( + protected string $overtime, + ) { + } +} diff --git a/src/Requests/Budget/DeleteBudget.php b/src/Requests/Budget/DeleteBudget.php new file mode 100644 index 0000000..39e076a --- /dev/null +++ b/src/Requests/Budget/DeleteBudget.php @@ -0,0 +1,30 @@ +budget}"; + } + + + /** + * @param string $budget + */ + public function __construct( + protected string $budget, + ) { + } +} diff --git a/src/Requests/Budget/GetBudget.php b/src/Requests/Budget/GetBudget.php new file mode 100644 index 0000000..26f1fa8 --- /dev/null +++ b/src/Requests/Budget/GetBudget.php @@ -0,0 +1,30 @@ +budget}"; + } + + + /** + * @param string $budget + */ + public function __construct( + protected string $budget, + ) { + } +} diff --git a/src/Requests/Budget/GetBudgets.php b/src/Requests/Budget/GetBudgets.php new file mode 100644 index 0000000..54626d9 --- /dev/null +++ b/src/Requests/Budget/GetBudgets.php @@ -0,0 +1,95 @@ + $this->filtercustomerId, + 'filter[customerId][eq]' => $this->filtercustomerIdeq, + 'filter[customerId][nq]' => $this->filtercustomerIdnq, + 'filter[customerId][gt]' => $this->filtercustomerIdgt, + 'filter[customerId][lt]' => $this->filtercustomerIdlt, + 'filter[customerId][gte]' => $this->filtercustomerIdgte, + 'filter[customerId][lte]' => $this->filtercustomerIdlte, + 'filter[customerId][contains]' => $this->filtercustomerIdcontains, + 'filter[budgetTypeId]' => $this->filterbudgetTypeId, + 'filter[budgetTypeId][eq]' => $this->filterbudgetTypeIdeq, + 'filter[budgetTypeId][nq]' => $this->filterbudgetTypeIdnq, + 'filter[budgetTypeId][gt]' => $this->filterbudgetTypeIdgt, + 'filter[budgetTypeId][lt]' => $this->filterbudgetTypeIdlt, + 'filter[budgetTypeId][gte]' => $this->filterbudgetTypeIdgte, + 'filter[budgetTypeId][lte]' => $this->filterbudgetTypeIdlte, + 'filter[budgetTypeId][contains]' => $this->filterbudgetTypeIdcontains, + 'filter[isArchived]' => $this->filterisArchived, + 'filter[customerExternalId]' => $this->filtercustomerExternalId, + 'filter[showToCustomer]' => $this->filtershowToCustomer, + 'include' => $this->include, + ]); + } +} diff --git a/src/Requests/Budget/PatchBudget.php b/src/Requests/Budget/PatchBudget.php new file mode 100644 index 0000000..443e208 --- /dev/null +++ b/src/Requests/Budget/PatchBudget.php @@ -0,0 +1,34 @@ +budget}"; + } + + + /** + * @param string $budget + */ + public function __construct( + protected string $budget, + ) { + } +} diff --git a/src/Requests/Budget/PostBudgets.php b/src/Requests/Budget/PostBudgets.php new file mode 100644 index 0000000..cca397c --- /dev/null +++ b/src/Requests/Budget/PostBudgets.php @@ -0,0 +1,30 @@ +budget}"; + } + + + /** + * @param string $budget + */ + public function __construct( + protected string $budget, + ) { + } +} diff --git a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php new file mode 100644 index 0000000..3f2278c --- /dev/null +++ b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php @@ -0,0 +1,38 @@ + $this->filterbudgetId, 'filter[budgetId][eq]' => $this->filterbudgetIdeq]); + } +} diff --git a/src/Requests/BudgetType/GetBudgetTypes.php b/src/Requests/BudgetType/GetBudgetTypes.php new file mode 100644 index 0000000..f165009 --- /dev/null +++ b/src/Requests/BudgetType/GetBudgetTypes.php @@ -0,0 +1,26 @@ +change}"; + } + + + /** + * @param string $change + */ + public function __construct( + protected string $change, + ) { + } +} diff --git a/src/Requests/Change/GetChanges.php b/src/Requests/Change/GetChanges.php new file mode 100644 index 0000000..92587c2 --- /dev/null +++ b/src/Requests/Change/GetChanges.php @@ -0,0 +1,26 @@ +correction}"; + } + + + /** + * @param string $correction + */ + public function __construct( + protected string $correction, + ) { + } +} diff --git a/src/Requests/Correction/PostCorrections.php b/src/Requests/Correction/PostCorrections.php new file mode 100644 index 0000000..af6e871 --- /dev/null +++ b/src/Requests/Correction/PostCorrections.php @@ -0,0 +1,30 @@ +correction}"; + } + + + /** + * @param string $correction + */ + public function __construct( + protected string $correction, + ) { + } +} diff --git a/src/Requests/Customer/DeleteCustomer.php b/src/Requests/Customer/DeleteCustomer.php new file mode 100644 index 0000000..f0a931e --- /dev/null +++ b/src/Requests/Customer/DeleteCustomer.php @@ -0,0 +1,30 @@ +customer}"; + } + + + /** + * @param string $customer + */ + public function __construct( + protected string $customer, + ) { + } +} diff --git a/src/Requests/Customer/GetCustomer.php b/src/Requests/Customer/GetCustomer.php new file mode 100644 index 0000000..3c0af74 --- /dev/null +++ b/src/Requests/Customer/GetCustomer.php @@ -0,0 +1,30 @@ +customer}"; + } + + + /** + * @param string $customer + */ + public function __construct( + protected string $customer, + ) { + } +} diff --git a/src/Requests/Customer/GetCustomers.php b/src/Requests/Customer/GetCustomers.php new file mode 100644 index 0000000..4582b71 --- /dev/null +++ b/src/Requests/Customer/GetCustomers.php @@ -0,0 +1,38 @@ + $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); + } +} diff --git a/src/Requests/Customer/PatchCustomer.php b/src/Requests/Customer/PatchCustomer.php new file mode 100644 index 0000000..1b2de81 --- /dev/null +++ b/src/Requests/Customer/PatchCustomer.php @@ -0,0 +1,34 @@ +customer}"; + } + + + /** + * @param string $customer + */ + public function __construct( + protected string $customer, + ) { + } +} diff --git a/src/Requests/Customer/PostCustomers.php b/src/Requests/Customer/PostCustomers.php new file mode 100644 index 0000000..2052484 --- /dev/null +++ b/src/Requests/Customer/PostCustomers.php @@ -0,0 +1,30 @@ +customer}"; + } + + + /** + * @param string $customer + */ + public function __construct( + protected string $customer, + ) { + } +} diff --git a/src/Requests/DailyProgress/GetDailyProgresses.php b/src/Requests/DailyProgress/GetDailyProgresses.php new file mode 100644 index 0000000..7596069 --- /dev/null +++ b/src/Requests/DailyProgress/GetDailyProgresses.php @@ -0,0 +1,26 @@ +budget}/entries-export"; + } + + + /** + * @param string $budget + * @param null|string $filteruserId + * @param null|string $filteruserIdeq + * @param null|string $filteruserIdnq + * @param null|string $filteruserIdgt + * @param null|string $filteruserIdlt + * @param null|string $filteruserIdgte + * @param null|string $filteruserIdlte + * @param null|string $filteruserIdcontains + * @param null|string $filterbudgetId + * @param null|string $filterbudgetIdeq + * @param null|string $filterbudgetIdnq + * @param null|string $filterbudgetIdgt + * @param null|string $filterbudgetIdlt + * @param null|string $filterbudgetIdgte + * @param null|string $filterbudgetIdlte + * @param null|string $filterbudgetIdcontains + * @param null|string $filterstartedAt + * @param null|string $filterstartedAteq + * @param null|string $filterstartedAtnq + * @param null|string $filterstartedAtgt + * @param null|string $filterstartedAtlt + * @param null|string $filterstartedAtgte + * @param null|string $filterstartedAtlte + * @param null|string $filterstartedAtcontains + * @param null|string $filterendedAt + * @param null|string $filterendedAteq + * @param null|string $filterendedAtnq + * @param null|string $filterendedAtgt + * @param null|string $filterendedAtlt + * @param null|string $filterendedAtgte + * @param null|string $filterendedAtlte + * @param null|string $filterendedAtcontains + * @param null|string $filterhasOvertime + * @param null|string $filterhasOvertimeeq + * @param null|string $filterhasOvertimenq + * @param null|string $filterhasOvertimegt + * @param null|string $filterhasOvertimelt + * @param null|string $filterhasOvertimegte + * @param null|string $filterhasOvertimelte + * @param null|string $filterhasOvertimecontains + * @param null|string $filteruserFullName + * @param null|string $filteruserFullNameeq + * @param null|string $filteruserFullNamenq + * @param null|string $filteruserFullNamegt + * @param null|string $filteruserFullNamelt + * @param null|string $filteruserFullNamegte + * @param null|string $filteruserFullNamelte + * @param null|string $filteruserFullNamecontains + * @param null|string $filtercustomerId + * @param null|string $filtercustomerIdeq + * @param null|string $filtercustomerIdnq + * @param null|string $filtercustomerIdgt + * @param null|string $filtercustomerIdlt + * @param null|string $filtercustomerIdgte + * @param null|string $filtercustomerIdlte + * @param null|string $filtercustomerIdcontains + * @param null|string $filterticketNumber + * @param null|string $filterticketNumbereq + * @param null|string $filterticketNumbernq + * @param null|string $filterticketNumbergt + * @param null|string $filterticketNumberlt + * @param null|string $filterticketNumbergte + * @param null|string $filterticketNumberlte + * @param null|string $filterticketNumbercontains + * @param null|string $filtersettlement + * @param null|string $filterisInvoiced + * @param null|string $filterisInvoiceable + * @param null|string $include + */ + public function __construct( + protected string $budget, + protected ?string $filteruserId = null, + protected ?string $filteruserIdeq = null, + protected ?string $filteruserIdnq = null, + protected ?string $filteruserIdgt = null, + protected ?string $filteruserIdlt = null, + protected ?string $filteruserIdgte = null, + protected ?string $filteruserIdlte = null, + protected ?string $filteruserIdcontains = null, + protected ?string $filterbudgetId = null, + protected ?string $filterbudgetIdeq = null, + protected ?string $filterbudgetIdnq = null, + protected ?string $filterbudgetIdgt = null, + protected ?string $filterbudgetIdlt = null, + protected ?string $filterbudgetIdgte = null, + protected ?string $filterbudgetIdlte = null, + protected ?string $filterbudgetIdcontains = null, + protected ?string $filterstartedAt = null, + protected ?string $filterstartedAteq = null, + protected ?string $filterstartedAtnq = null, + protected ?string $filterstartedAtgt = null, + protected ?string $filterstartedAtlt = null, + protected ?string $filterstartedAtgte = null, + protected ?string $filterstartedAtlte = null, + protected ?string $filterstartedAtcontains = null, + protected ?string $filterendedAt = null, + protected ?string $filterendedAteq = null, + protected ?string $filterendedAtnq = null, + protected ?string $filterendedAtgt = null, + protected ?string $filterendedAtlt = null, + protected ?string $filterendedAtgte = null, + protected ?string $filterendedAtlte = null, + protected ?string $filterendedAtcontains = null, + protected ?string $filterhasOvertime = null, + protected ?string $filterhasOvertimeeq = null, + protected ?string $filterhasOvertimenq = null, + protected ?string $filterhasOvertimegt = null, + protected ?string $filterhasOvertimelt = null, + protected ?string $filterhasOvertimegte = null, + protected ?string $filterhasOvertimelte = null, + protected ?string $filterhasOvertimecontains = null, + protected ?string $filteruserFullName = null, + protected ?string $filteruserFullNameeq = null, + protected ?string $filteruserFullNamenq = null, + protected ?string $filteruserFullNamegt = null, + protected ?string $filteruserFullNamelt = null, + protected ?string $filteruserFullNamegte = null, + protected ?string $filteruserFullNamelte = null, + protected ?string $filteruserFullNamecontains = null, + protected ?string $filtercustomerId = null, + protected ?string $filtercustomerIdeq = null, + protected ?string $filtercustomerIdnq = null, + protected ?string $filtercustomerIdgt = null, + protected ?string $filtercustomerIdlt = null, + protected ?string $filtercustomerIdgte = null, + protected ?string $filtercustomerIdlte = null, + protected ?string $filtercustomerIdcontains = null, + protected ?string $filterticketNumber = null, + protected ?string $filterticketNumbereq = null, + protected ?string $filterticketNumbernq = null, + protected ?string $filterticketNumbergt = null, + protected ?string $filterticketNumberlt = null, + protected ?string $filterticketNumbergte = null, + protected ?string $filterticketNumberlte = null, + protected ?string $filterticketNumbercontains = null, + protected ?string $filtersettlement = null, + protected ?string $filterisInvoiced = null, + protected ?string $filterisInvoiceable = null, + protected ?string $include = null, + ) { + } + + + public function defaultQuery(): array + { + return array_filter([ + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + 'filter[userId][nq]' => $this->filteruserIdnq, + 'filter[userId][gt]' => $this->filteruserIdgt, + 'filter[userId][lt]' => $this->filteruserIdlt, + 'filter[userId][gte]' => $this->filteruserIdgte, + 'filter[userId][lte]' => $this->filteruserIdlte, + 'filter[userId][contains]' => $this->filteruserIdcontains, + 'filter[budgetId]' => $this->filterbudgetId, + 'filter[budgetId][eq]' => $this->filterbudgetIdeq, + 'filter[budgetId][nq]' => $this->filterbudgetIdnq, + 'filter[budgetId][gt]' => $this->filterbudgetIdgt, + 'filter[budgetId][lt]' => $this->filterbudgetIdlt, + 'filter[budgetId][gte]' => $this->filterbudgetIdgte, + 'filter[budgetId][lte]' => $this->filterbudgetIdlte, + 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, + 'filter[startedAt]' => $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[hasOvertime]' => $this->filterhasOvertime, + 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, + 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, + 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, + 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, + 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, + 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, + 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, + 'filter[userFullName]' => $this->filteruserFullName, + 'filter[userFullName][eq]' => $this->filteruserFullNameeq, + 'filter[userFullName][nq]' => $this->filteruserFullNamenq, + 'filter[userFullName][gt]' => $this->filteruserFullNamegt, + 'filter[userFullName][lt]' => $this->filteruserFullNamelt, + 'filter[userFullName][gte]' => $this->filteruserFullNamegte, + 'filter[userFullName][lte]' => $this->filteruserFullNamelte, + 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, + 'filter[customerId]' => $this->filtercustomerId, + 'filter[customerId][eq]' => $this->filtercustomerIdeq, + 'filter[customerId][nq]' => $this->filtercustomerIdnq, + 'filter[customerId][gt]' => $this->filtercustomerIdgt, + 'filter[customerId][lt]' => $this->filtercustomerIdlt, + 'filter[customerId][gte]' => $this->filtercustomerIdgte, + 'filter[customerId][lte]' => $this->filtercustomerIdlte, + 'filter[customerId][contains]' => $this->filtercustomerIdcontains, + 'filter[ticketNumber]' => $this->filterticketNumber, + 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, + 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, + 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, + 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, + 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, + 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, + 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, + 'filter[settlement]' => $this->filtersettlement, + 'filter[isInvoiced]' => $this->filterisInvoiced, + 'filter[isInvoiceable]' => $this->filterisInvoiceable, + 'include' => $this->include, + ]); + } +} diff --git a/src/Requests/Entry/DeleteEntry.php b/src/Requests/Entry/DeleteEntry.php new file mode 100644 index 0000000..fdb2009 --- /dev/null +++ b/src/Requests/Entry/DeleteEntry.php @@ -0,0 +1,30 @@ +entry}"; + } + + + /** + * @param string $entry + */ + public function __construct( + protected string $entry, + ) { + } +} diff --git a/src/Requests/Entry/GetEntries.php b/src/Requests/Entry/GetEntries.php new file mode 100644 index 0000000..b689fcf --- /dev/null +++ b/src/Requests/Entry/GetEntries.php @@ -0,0 +1,239 @@ + $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + 'filter[userId][nq]' => $this->filteruserIdnq, + 'filter[userId][gt]' => $this->filteruserIdgt, + 'filter[userId][lt]' => $this->filteruserIdlt, + 'filter[userId][gte]' => $this->filteruserIdgte, + 'filter[userId][lte]' => $this->filteruserIdlte, + 'filter[userId][contains]' => $this->filteruserIdcontains, + 'filter[budgetId]' => $this->filterbudgetId, + 'filter[budgetId][eq]' => $this->filterbudgetIdeq, + 'filter[budgetId][nq]' => $this->filterbudgetIdnq, + 'filter[budgetId][gt]' => $this->filterbudgetIdgt, + 'filter[budgetId][lt]' => $this->filterbudgetIdlt, + 'filter[budgetId][gte]' => $this->filterbudgetIdgte, + 'filter[budgetId][lte]' => $this->filterbudgetIdlte, + 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, + 'filter[startedAt]' => $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[hasOvertime]' => $this->filterhasOvertime, + 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, + 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, + 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, + 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, + 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, + 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, + 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, + 'filter[userFullName]' => $this->filteruserFullName, + 'filter[userFullName][eq]' => $this->filteruserFullNameeq, + 'filter[userFullName][nq]' => $this->filteruserFullNamenq, + 'filter[userFullName][gt]' => $this->filteruserFullNamegt, + 'filter[userFullName][lt]' => $this->filteruserFullNamelt, + 'filter[userFullName][gte]' => $this->filteruserFullNamegte, + 'filter[userFullName][lte]' => $this->filteruserFullNamelte, + 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, + 'filter[customerId]' => $this->filtercustomerId, + 'filter[customerId][eq]' => $this->filtercustomerIdeq, + 'filter[customerId][nq]' => $this->filtercustomerIdnq, + 'filter[customerId][gt]' => $this->filtercustomerIdgt, + 'filter[customerId][lt]' => $this->filtercustomerIdlt, + 'filter[customerId][gte]' => $this->filtercustomerIdgte, + 'filter[customerId][lte]' => $this->filtercustomerIdlte, + 'filter[customerId][contains]' => $this->filtercustomerIdcontains, + 'filter[ticketNumber]' => $this->filterticketNumber, + 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, + 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, + 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, + 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, + 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, + 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, + 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, + 'filter[settlement]' => $this->filtersettlement, + 'filter[isInvoiced]' => $this->filterisInvoiced, + 'filter[isInvoiceable]' => $this->filterisInvoiceable, + 'include' => $this->include, + ]); + } +} diff --git a/src/Requests/Entry/GetEntry.php b/src/Requests/Entry/GetEntry.php new file mode 100644 index 0000000..c75ca7b --- /dev/null +++ b/src/Requests/Entry/GetEntry.php @@ -0,0 +1,30 @@ +entry}"; + } + + + /** + * @param string $entry + */ + public function __construct( + protected string $entry, + ) { + } +} diff --git a/src/Requests/Entry/PatchEntry.php b/src/Requests/Entry/PatchEntry.php new file mode 100644 index 0000000..5ffc8b7 --- /dev/null +++ b/src/Requests/Entry/PatchEntry.php @@ -0,0 +1,34 @@ +entry}"; + } + + + /** + * @param string $entry + */ + public function __construct( + protected string $entry, + ) { + } +} diff --git a/src/Requests/Entry/PostEntries.php b/src/Requests/Entry/PostEntries.php new file mode 100644 index 0000000..e786fa7 --- /dev/null +++ b/src/Requests/Entry/PostEntries.php @@ -0,0 +1,30 @@ +entry}"; + } + + + /** + * @param string $entry + */ + public function __construct( + protected string $entry, + ) { + } +} diff --git a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php b/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php new file mode 100644 index 0000000..c4632ae --- /dev/null +++ b/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php @@ -0,0 +1,30 @@ +entrySuggestion}"; + } + + + /** + * @param string $entrySuggestion + */ + public function __construct( + protected string $entrySuggestion, + ) { + } +} diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestion.php b/src/Requests/EntrySuggestion/GetEntrySuggestion.php new file mode 100644 index 0000000..73acd2d --- /dev/null +++ b/src/Requests/EntrySuggestion/GetEntrySuggestion.php @@ -0,0 +1,30 @@ +entrySuggestion}"; + } + + + /** + * @param string $entrySuggestion + */ + public function __construct( + protected string $entrySuggestion, + ) { + } +} diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestions.php b/src/Requests/EntrySuggestion/GetEntrySuggestions.php new file mode 100644 index 0000000..d51874c --- /dev/null +++ b/src/Requests/EntrySuggestion/GetEntrySuggestions.php @@ -0,0 +1,59 @@ + $this->filterdate, + 'filter[date][eq]' => $this->filterdateeq, + 'filter[date][nq]' => $this->filterdatenq, + 'filter[date][gt]' => $this->filterdategt, + 'filter[date][lt]' => $this->filterdatelt, + 'filter[date][gte]' => $this->filterdategte, + 'filter[date][lte]' => $this->filterdatelte, + 'filter[date][contains]' => $this->filterdatecontains, + ]); + } +} diff --git a/src/Requests/Event/PostEvents.php b/src/Requests/Event/PostEvents.php new file mode 100644 index 0000000..45c9247 --- /dev/null +++ b/src/Requests/Event/PostEvents.php @@ -0,0 +1,30 @@ +incident}"; + } + + + /** + * @param string $incident + */ + public function __construct( + protected string $incident, + ) { + } +} diff --git a/src/Requests/Incident/GetIncidents.php b/src/Requests/Incident/GetIncidents.php new file mode 100644 index 0000000..16f4b86 --- /dev/null +++ b/src/Requests/Incident/GetIncidents.php @@ -0,0 +1,26 @@ +overtime}/mark-as-exported"; + } + + + /** + * @param string $overtime + */ + public function __construct( + protected string $overtime, + ) { + } +} diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php new file mode 100644 index 0000000..1d3ff00 --- /dev/null +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php @@ -0,0 +1,34 @@ +entry}/mark-as-invoiced"; + } + + + /** + * @param string $entry + */ + public function __construct( + protected string $entry, + ) { + } +} diff --git a/src/Requests/Me/GetMes.php b/src/Requests/Me/GetMes.php new file mode 100644 index 0000000..ee2df76 --- /dev/null +++ b/src/Requests/Me/GetMes.php @@ -0,0 +1,26 @@ +incident}"; + } + + + /** + * @param string $incident + */ + public function __construct( + protected string $incident, + ) { + } +} diff --git a/src/Requests/Overtime/GetOvertimes.php b/src/Requests/Overtime/GetOvertimes.php new file mode 100644 index 0000000..4aff87f --- /dev/null +++ b/src/Requests/Overtime/GetOvertimes.php @@ -0,0 +1,113 @@ + $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[isApproved]' => $this->filterisApproved, + 'filter[approvedAt]' => $this->filterapprovedAt, + 'filter[approvedAt][eq]' => $this->filterapprovedAteq, + 'filter[approvedAt][nq]' => $this->filterapprovedAtnq, + 'filter[approvedAt][gt]' => $this->filterapprovedAtgt, + 'filter[approvedAt][lt]' => $this->filterapprovedAtlt, + 'filter[approvedAt][gte]' => $this->filterapprovedAtgte, + 'filter[approvedAt][lte]' => $this->filterapprovedAtlte, + 'filter[approvedAt][contains]' => $this->filterapprovedAtcontains, + 'filter[isExported]' => $this->filterisExported, + ]); + } +} diff --git a/src/Requests/Period/GetBudgetPeriods.php b/src/Requests/Period/GetBudgetPeriods.php new file mode 100644 index 0000000..5ea6a8f --- /dev/null +++ b/src/Requests/Period/GetBudgetPeriods.php @@ -0,0 +1,30 @@ +budget}/periods"; + } + + + /** + * @param string $budget + */ + public function __construct( + protected string $budget, + ) { + } +} diff --git a/src/Requests/Team/DeleteTeam.php b/src/Requests/Team/DeleteTeam.php new file mode 100644 index 0000000..96a12a5 --- /dev/null +++ b/src/Requests/Team/DeleteTeam.php @@ -0,0 +1,30 @@ +team}"; + } + + + /** + * @param string $team + */ + public function __construct( + protected string $team, + ) { + } +} diff --git a/src/Requests/Team/GetTeam.php b/src/Requests/Team/GetTeam.php new file mode 100644 index 0000000..ca76ca2 --- /dev/null +++ b/src/Requests/Team/GetTeam.php @@ -0,0 +1,30 @@ +team}"; + } + + + /** + * @param string $team + */ + public function __construct( + protected string $team, + ) { + } +} diff --git a/src/Requests/Team/GetTeams.php b/src/Requests/Team/GetTeams.php new file mode 100644 index 0000000..56c4a08 --- /dev/null +++ b/src/Requests/Team/GetTeams.php @@ -0,0 +1,26 @@ +team}"; + } + + + /** + * @param string $team + */ + public function __construct( + protected string $team, + ) { + } +} diff --git a/src/Requests/Team/PostTeams.php b/src/Requests/Team/PostTeams.php new file mode 100644 index 0000000..2b359df --- /dev/null +++ b/src/Requests/Team/PostTeams.php @@ -0,0 +1,30 @@ +team}"; + } + + + /** + * @param string $team + */ + public function __construct( + protected string $team, + ) { + } +} diff --git a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php b/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php new file mode 100644 index 0000000..64bb3e6 --- /dev/null +++ b/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php @@ -0,0 +1,53 @@ + $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[teamId]' => $this->filterteamId, + 'filter[teamId][eq]' => $this->filterteamIdeq, + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + ]); + } +} diff --git a/src/Requests/User/DeleteUser.php b/src/Requests/User/DeleteUser.php new file mode 100644 index 0000000..d2a17ad --- /dev/null +++ b/src/Requests/User/DeleteUser.php @@ -0,0 +1,30 @@ +user}"; + } + + + /** + * @param string $user + */ + public function __construct( + protected string $user, + ) { + } +} diff --git a/src/Requests/User/GetUser.php b/src/Requests/User/GetUser.php new file mode 100644 index 0000000..e7f8118 --- /dev/null +++ b/src/Requests/User/GetUser.php @@ -0,0 +1,30 @@ +user}"; + } + + + /** + * @param string $user + */ + public function __construct( + protected string $user, + ) { + } +} diff --git a/src/Requests/User/GetUsers.php b/src/Requests/User/GetUsers.php new file mode 100644 index 0000000..b9e3445 --- /dev/null +++ b/src/Requests/User/GetUsers.php @@ -0,0 +1,38 @@ + $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); + } +} diff --git a/src/Requests/User/PatchUser.php b/src/Requests/User/PatchUser.php new file mode 100644 index 0000000..072ea34 --- /dev/null +++ b/src/Requests/User/PatchUser.php @@ -0,0 +1,34 @@ +user}"; + } + + + /** + * @param string $user + */ + public function __construct( + protected string $user, + ) { + } +} diff --git a/src/Requests/User/PostUsers.php b/src/Requests/User/PostUsers.php new file mode 100644 index 0000000..a54a7f3 --- /dev/null +++ b/src/Requests/User/PostUsers.php @@ -0,0 +1,30 @@ +user}"; + } + + + /** + * @param string $user + */ + public function __construct( + protected string $user, + ) { + } +} diff --git a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php new file mode 100644 index 0000000..632d4f2 --- /dev/null +++ b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php @@ -0,0 +1,131 @@ + $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[teamId]' => $this->filterteamId, + 'filter[teamId][eq]' => $this->filterteamIdeq, + 'filter[teamId][nq]' => $this->filterteamIdnq, + 'filter[teamId][gt]' => $this->filterteamIdgt, + 'filter[teamId][lt]' => $this->filterteamIdlt, + 'filter[teamId][gte]' => $this->filterteamIdgte, + 'filter[teamId][lte]' => $this->filterteamIdlte, + 'filter[teamId][contains]' => $this->filterteamIdcontains, + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + 'filter[userId][nq]' => $this->filteruserIdnq, + 'filter[userId][gt]' => $this->filteruserIdgt, + 'filter[userId][lt]' => $this->filteruserIdlt, + 'filter[userId][gte]' => $this->filteruserIdgte, + 'filter[userId][lte]' => $this->filteruserIdlte, + 'filter[userId][contains]' => $this->filteruserIdcontains, + ]); + } +} diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php new file mode 100644 index 0000000..857b072 --- /dev/null +++ b/src/Resource/Approve.php @@ -0,0 +1,18 @@ +connector->send(new PostOvertimeApprove($overtime)); + } +} diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php new file mode 100644 index 0000000..f1a2b36 --- /dev/null +++ b/src/Resource/Budget.php @@ -0,0 +1,105 @@ +connector->send(new GetBudgets($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); + } + + + public function postBudgets(): Response + { + return $this->connector->send(new PostBudgets()); + } + + + /** + * @param string $budget + */ + public function getBudget(string $budget): Response + { + return $this->connector->send(new GetBudget($budget)); + } + + + /** + * @param string $budget + */ + public function putBudget(string $budget): Response + { + return $this->connector->send(new PutBudget($budget)); + } + + + /** + * @param string $budget + */ + public function deleteBudget(string $budget): Response + { + return $this->connector->send(new DeleteBudget($budget)); + } + + + /** + * @param string $budget + */ + public function patchBudget(string $budget): Response + { + return $this->connector->send(new PatchBudget($budget)); + } +} diff --git a/src/Resource/BudgetTimeSpentTotal.php b/src/Resource/BudgetTimeSpentTotal.php new file mode 100644 index 0000000..83b50b5 --- /dev/null +++ b/src/Resource/BudgetTimeSpentTotal.php @@ -0,0 +1,19 @@ +connector->send(new GetBudgetTimeSpentTotals($filterbudgetId, $filterbudgetIdeq)); + } +} diff --git a/src/Resource/BudgetType.php b/src/Resource/BudgetType.php new file mode 100644 index 0000000..10a0141 --- /dev/null +++ b/src/Resource/BudgetType.php @@ -0,0 +1,15 @@ +connector->send(new GetBudgetTypes()); + } +} diff --git a/src/Resource/Change.php b/src/Resource/Change.php new file mode 100644 index 0000000..bf245c3 --- /dev/null +++ b/src/Resource/Change.php @@ -0,0 +1,25 @@ +connector->send(new GetChange($change)); + } + + + public function getChanges(): Response + { + return $this->connector->send(new GetChanges()); + } +} diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php new file mode 100644 index 0000000..6d3112f --- /dev/null +++ b/src/Resource/Correction.php @@ -0,0 +1,35 @@ +connector->send(new PostCorrections()); + } + + + /** + * @param string $correction + */ + public function putCorrection(string $correction): Response + { + return $this->connector->send(new PutCorrection($correction)); + } + + + /** + * @param string $correction + */ + public function patchCorrection(string $correction): Response + { + return $this->connector->send(new PatchCorrection($correction)); + } +} diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php new file mode 100644 index 0000000..a568595 --- /dev/null +++ b/src/Resource/Customer.php @@ -0,0 +1,66 @@ +connector->send(new GetCustomers($filterexternalId, $filterexternalIdeq)); + } + + + public function postCustomers(): Response + { + return $this->connector->send(new PostCustomers()); + } + + + /** + * @param string $customer + */ + public function getCustomer(string $customer): Response + { + return $this->connector->send(new GetCustomer($customer)); + } + + + /** + * @param string $customer + */ + public function putCustomer(string $customer): Response + { + return $this->connector->send(new PutCustomer($customer)); + } + + + /** + * @param string $customer + */ + public function deleteCustomer(string $customer): Response + { + return $this->connector->send(new DeleteCustomer($customer)); + } + + + /** + * @param string $customer + */ + public function patchCustomer(string $customer): Response + { + return $this->connector->send(new PatchCustomer($customer)); + } +} diff --git a/src/Resource/DailyProgress.php b/src/Resource/DailyProgress.php new file mode 100644 index 0000000..f5d2805 --- /dev/null +++ b/src/Resource/DailyProgress.php @@ -0,0 +1,15 @@ +connector->send(new GetDailyProgresses()); + } +} diff --git a/src/Resource/EntriesExport.php b/src/Resource/EntriesExport.php new file mode 100644 index 0000000..bbbc72f --- /dev/null +++ b/src/Resource/EntriesExport.php @@ -0,0 +1,156 @@ +connector->send(new GetBudgetEntriesExport($budget, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + } +} diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php new file mode 100644 index 0000000..4a03d14 --- /dev/null +++ b/src/Resource/Entry.php @@ -0,0 +1,201 @@ +connector->send(new GetEntries($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + } + + + public function postEntries(): Response + { + return $this->connector->send(new PostEntries()); + } + + + /** + * @param string $entry + */ + public function getEntry(string $entry): Response + { + return $this->connector->send(new GetEntry($entry)); + } + + + /** + * @param string $entry + */ + public function putEntry(string $entry): Response + { + return $this->connector->send(new PutEntry($entry)); + } + + + /** + * @param string $entry + */ + public function deleteEntry(string $entry): Response + { + return $this->connector->send(new DeleteEntry($entry)); + } + + + /** + * @param string $entry + */ + public function patchEntry(string $entry): Response + { + return $this->connector->send(new PatchEntry($entry)); + } +} diff --git a/src/Resource/EntrySuggestion.php b/src/Resource/EntrySuggestion.php new file mode 100644 index 0000000..3eda02a --- /dev/null +++ b/src/Resource/EntrySuggestion.php @@ -0,0 +1,54 @@ +connector->send(new GetEntrySuggestions($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); + } + + + /** + * @param string $entrySuggestion + */ + public function getEntrySuggestion(string $entrySuggestion): Response + { + return $this->connector->send(new GetEntrySuggestion($entrySuggestion)); + } + + + /** + * @param string $entrySuggestion + */ + public function deleteEntrySuggestion(string $entrySuggestion): Response + { + return $this->connector->send(new DeleteEntrySuggestion($entrySuggestion)); + } +} diff --git a/src/Resource/Event.php b/src/Resource/Event.php new file mode 100644 index 0000000..d898701 --- /dev/null +++ b/src/Resource/Event.php @@ -0,0 +1,15 @@ +connector->send(new PostEvents()); + } +} diff --git a/src/Resource/ExportMail.php b/src/Resource/ExportMail.php new file mode 100644 index 0000000..88d5f6d --- /dev/null +++ b/src/Resource/ExportMail.php @@ -0,0 +1,15 @@ +connector->send(new GetBudgetsExportMails()); + } +} diff --git a/src/Resource/Incident.php b/src/Resource/Incident.php new file mode 100644 index 0000000..06fa6b5 --- /dev/null +++ b/src/Resource/Incident.php @@ -0,0 +1,25 @@ +connector->send(new GetIncident($incident)); + } + + + public function getIncidents(): Response + { + return $this->connector->send(new GetIncidents()); + } +} diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php new file mode 100644 index 0000000..3816e37 --- /dev/null +++ b/src/Resource/MarkAsExported.php @@ -0,0 +1,18 @@ +connector->send(new PostOvertimeMarkAsExported($overtime)); + } +} diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php new file mode 100644 index 0000000..6a3fa48 --- /dev/null +++ b/src/Resource/MarkAsInvoiced.php @@ -0,0 +1,18 @@ +connector->send(new PostEntryMarkAsInvoiced($entry)); + } +} diff --git a/src/Resource/Me.php b/src/Resource/Me.php new file mode 100644 index 0000000..3c58ca2 --- /dev/null +++ b/src/Resource/Me.php @@ -0,0 +1,15 @@ +connector->send(new GetMes()); + } +} diff --git a/src/Resource/Number.php b/src/Resource/Number.php new file mode 100644 index 0000000..f7435ff --- /dev/null +++ b/src/Resource/Number.php @@ -0,0 +1,18 @@ +connector->send(new GetIncidentsNumber($incident)); + } +} diff --git a/src/Resource/Overtime.php b/src/Resource/Overtime.php new file mode 100644 index 0000000..8d417f2 --- /dev/null +++ b/src/Resource/Overtime.php @@ -0,0 +1,70 @@ +connector->send(new GetOvertimes($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); + } +} diff --git a/src/Resource/Period.php b/src/Resource/Period.php new file mode 100644 index 0000000..7b47d72 --- /dev/null +++ b/src/Resource/Period.php @@ -0,0 +1,18 @@ +connector->send(new GetBudgetPeriods($budget)); + } +} diff --git a/src/Resource/Team.php b/src/Resource/Team.php new file mode 100644 index 0000000..6e72ccf --- /dev/null +++ b/src/Resource/Team.php @@ -0,0 +1,62 @@ +connector->send(new GetTeams()); + } + + + public function postTeams(): Response + { + return $this->connector->send(new PostTeams()); + } + + + /** + * @param string $team + */ + public function getTeam(string $team): Response + { + return $this->connector->send(new GetTeam($team)); + } + + + /** + * @param string $team + */ + public function putTeam(string $team): Response + { + return $this->connector->send(new PutTeam($team)); + } + + + /** + * @param string $team + */ + public function deleteTeam(string $team): Response + { + return $this->connector->send(new DeleteTeam($team)); + } + + + /** + * @param string $team + */ + public function patchTeam(string $team): Response + { + return $this->connector->send(new PatchTeam($team)); + } +} diff --git a/src/Resource/TimeSpentTotal.php b/src/Resource/TimeSpentTotal.php new file mode 100644 index 0000000..2dcdcf9 --- /dev/null +++ b/src/Resource/TimeSpentTotal.php @@ -0,0 +1,30 @@ +connector->send(new GetTimeSpentTotals($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); + } +} diff --git a/src/Resource/User.php b/src/Resource/User.php new file mode 100644 index 0000000..af3cbcb --- /dev/null +++ b/src/Resource/User.php @@ -0,0 +1,66 @@ +connector->send(new GetUsers($filterexternalId, $filterexternalIdeq)); + } + + + public function postUsers(): Response + { + return $this->connector->send(new PostUsers()); + } + + + /** + * @param string $user + */ + public function getUser(string $user): Response + { + return $this->connector->send(new GetUser($user)); + } + + + /** + * @param string $user + */ + public function putUser(string $user): Response + { + return $this->connector->send(new PutUser($user)); + } + + + /** + * @param string $user + */ + public function deleteUser(string $user): Response + { + return $this->connector->send(new DeleteUser($user)); + } + + + /** + * @param string $user + */ + public function patchUser(string $user): Response + { + return $this->connector->send(new PatchUser($user)); + } +} diff --git a/src/Resource/UserCustomerHoursAggregate.php b/src/Resource/UserCustomerHoursAggregate.php new file mode 100644 index 0000000..b9653d6 --- /dev/null +++ b/src/Resource/UserCustomerHoursAggregate.php @@ -0,0 +1,82 @@ +connector->send(new GetUserCustomerHoursAggregates($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); + } +} diff --git a/src/Timatic.php b/src/Timatic.php new file mode 100644 index 0000000..c0442f7 --- /dev/null +++ b/src/Timatic.php @@ -0,0 +1,189 @@ + Date: Sat, 15 Nov 2025 21:18:07 +0100 Subject: [PATCH 02/62] add tests --- README.md | 100 +++++++++++- composer.json | 21 ++- config/timatic.php | 43 +++++ example.php | 4 +- generator/JsonApiPestTestGenerator.php | 153 ++++++++++++++++++ generator/JsonApiRequestGenerator.php | 69 ++++++++ generator/generate.php | 34 +++- generator/stubs/pest-jsonapi-test-func.stub | 36 +++++ src/Facades/Timatic.php | 35 ++++ src/Foundation/Model.php | 15 ++ src/Providers/TimaticServiceProvider.php | 63 ++++++++ src/Requests/Approve/PostOvertimeApprove.php | 10 ++ src/Requests/Budget/DeleteBudget.php | 1 + src/Requests/Budget/GetBudget.php | 1 + src/Requests/Budget/GetBudgets.php | 1 + src/Requests/Budget/PatchBudget.php | 10 ++ src/Requests/Budget/PostBudgets.php | 12 +- src/Requests/Budget/PutBudget.php | 10 ++ .../GetBudgetTimeSpentTotals.php | 1 + src/Requests/BudgetType/GetBudgetTypes.php | 1 + src/Requests/Change/GetChange.php | 1 + src/Requests/Change/GetChanges.php | 1 + src/Requests/Correction/PatchCorrection.php | 10 ++ src/Requests/Correction/PostCorrections.php | 12 +- src/Requests/Correction/PutCorrection.php | 10 ++ src/Requests/Customer/DeleteCustomer.php | 1 + src/Requests/Customer/GetCustomer.php | 1 + src/Requests/Customer/GetCustomers.php | 1 + src/Requests/Customer/PatchCustomer.php | 10 ++ src/Requests/Customer/PostCustomers.php | 12 +- src/Requests/Customer/PutCustomer.php | 10 ++ .../DailyProgress/GetDailyProgresses.php | 1 + .../EntriesExport/GetBudgetEntriesExport.php | 1 + src/Requests/Entry/DeleteEntry.php | 1 + src/Requests/Entry/GetEntries.php | 1 + src/Requests/Entry/GetEntry.php | 1 + src/Requests/Entry/PatchEntry.php | 10 ++ src/Requests/Entry/PostEntries.php | 12 +- src/Requests/Entry/PutEntry.php | 10 ++ .../EntrySuggestion/DeleteEntrySuggestion.php | 1 + .../EntrySuggestion/GetEntrySuggestion.php | 1 + .../EntrySuggestion/GetEntrySuggestions.php | 1 + src/Requests/Event/PostEvents.php | 12 +- .../ExportMail/GetBudgetsExportMails.php | 1 + src/Requests/Incident/GetIncident.php | 1 + src/Requests/Incident/GetIncidents.php | 1 + .../PostOvertimeMarkAsExported.php | 10 ++ .../PostEntryMarkAsInvoiced.php | 10 ++ src/Requests/Me/GetMes.php | 1 + src/Requests/Number/GetIncidentsNumber.php | 1 + src/Requests/Overtime/GetOvertimes.php | 1 + src/Requests/Period/GetBudgetPeriods.php | 1 + src/Requests/Team/DeleteTeam.php | 1 + src/Requests/Team/GetTeam.php | 1 + src/Requests/Team/GetTeams.php | 1 + src/Requests/Team/PatchTeam.php | 10 ++ src/Requests/Team/PostTeams.php | 12 +- src/Requests/Team/PutTeam.php | 10 ++ .../TimeSpentTotal/GetTimeSpentTotals.php | 1 + src/Requests/User/DeleteUser.php | 1 + src/Requests/User/GetUser.php | 1 + src/Requests/User/GetUsers.php | 1 + src/Requests/User/PatchUser.php | 10 ++ src/Requests/User/PostUsers.php | 12 +- src/Requests/User/PutUser.php | 10 ++ .../GetUserCustomerHoursAggregates.php | 1 + src/{Timatic.php => TimaticConnector.php} | 2 +- tests/JsonApiBodyTest.php | 79 +++++++++ tests/Pest.php | 7 + 69 files changed, 907 insertions(+), 21 deletions(-) create mode 100644 config/timatic.php create mode 100644 generator/JsonApiPestTestGenerator.php create mode 100644 generator/JsonApiRequestGenerator.php create mode 100644 generator/stubs/pest-jsonapi-test-func.stub create mode 100644 src/Facades/Timatic.php create mode 100644 src/Providers/TimaticServiceProvider.php rename src/{Timatic.php => TimaticConnector.php} (98%) create mode 100644 tests/JsonApiBodyTest.php create mode 100644 tests/Pest.php diff --git a/README.md b/README.md index 0907698..68d0426 100644 --- a/README.md +++ b/README.md @@ -10,11 +10,13 @@ composer require timatic/php-sdk ## Usage +### Standalone PHP + ```php -use Timatic\SDK\Timatic; +use Timatic\SDK\TimaticConnector; // Initialize the SDK -$timatic = new Timatic(); +$timatic = new TimaticConnector(); // Example: Get users $response = $timatic->user()->getUsers(); @@ -24,11 +26,97 @@ $users = $response->json(); $response = $timatic->customer()->getCustomer(id: 1); $customer = $response->json(); -// Example: Create a new budget -$response = $timatic->budget()->postBudgets([ - 'name' => 'Q1 2024 Budget', - // ... other budget data +// Example: Create a new budget with a Model +use Timatic\SDK\Dto\Budget; + +$budget = new Budget([ + 'title' => 'Q1 2024 Budget', + 'totalPrice' => '50000', ]); + +$response = $timatic->send(new \Timatic\SDK\Requests\Budget\PostBudgets($budget)); +``` + +### Laravel + +The package automatically registers itself via Laravel auto-discovery. + +#### Configuration + +Publish the config file: + +```bash +php artisan vendor:publish --tag=timatic-config +``` + +Add your API credentials to `.env`: + +```env +TIMATIC_BASE_URL=https://api.app.timatic.test +TIMATIC_API_TOKEN=your-api-token-here +``` + +#### Using Dependency Injection (Recommended) + +The SDK connector is automatically registered in Laravel's service container, making it easy to inject into your controllers, commands, and other classes: + +```php +use Timatic\SDK\TimaticConnector; +use Timatic\SDK\Requests\BudgetType\GetBudgetTypeCollection; + +class BudgetController extends Controller +{ + public function __construct( + protected TimaticConnector $timatic + ) {} + + public function index() + { + // Using resource methods + $budgets = $this->timatic->budget()->getBudgets()->dto(); + + // Using direct send() with dtoOrFail() for automatic DTO conversion + $budgetTypes = $this->timatic + ->send(new GetBudgetTypeCollection()) + ->dtoOrFail(); + + return view('budgets.index', compact('budgets', 'budgetTypes')); + } + + public function store(Request $request) + { + $budget = new \Timatic\SDK\Dto\Budget([ + 'title' => $request->input('title'), + 'totalPrice' => $request->input('total_price'), + ]); + + $created = $this->timatic + ->send(new \Timatic\SDK\Requests\Budget\PostBudgets($budget)) + ->dtoOrFail(); + + return redirect()->route('budgets.show', $created->id); + } +} +``` + +**In Console Commands:** + +```php +use Timatic\SDK\TimaticConnector; + +class SyncBudgetsCommand extends Command +{ + public function handle(TimaticConnector $timatic): int + { + $budgets = $timatic->budget()->getBudgets()->dto(); + + foreach ($budgets as $budget) { + // Process budgets + } + + return Command::SUCCESS; + } +} ``` ## Available Resources diff --git a/composer.json b/composer.json index 346289a..3285835 100644 --- a/composer.json +++ b/composer.json @@ -23,8 +23,27 @@ "require-dev": { "laravel/boost": "^1.8", "phpunit/phpunit": "^10.0|^11.0", + "pestphp/pest": "^2.0|^3.0", "crescat-io/saloon-sdk-generator": "^1.0" }, + "scripts": { + "test": "pest" + }, + "extra": { + "laravel": { + "providers": [ + "Timatic\\SDK\\Providers\\TimaticServiceProvider" + ], + "aliases": { + "Timatic": "Timatic\\SDK\\Facades\\Timatic" + } + } + }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } + } } diff --git a/config/timatic.php b/config/timatic.php new file mode 100644 index 0000000..0fd2a7b --- /dev/null +++ b/config/timatic.php @@ -0,0 +1,43 @@ + env('TIMATIC_BASE_URL', 'https://api.app.timatic.test'), + + /* + |-------------------------------------------------------------------------- + | Timatic API Token + |-------------------------------------------------------------------------- + | + | Your Timatic API authentication token. This will be sent as a Bearer + | token in the Authorization header. + | + */ + + 'api_token' => env('TIMATIC_API_TOKEN'), + + /* + |-------------------------------------------------------------------------- + | Default Headers + |-------------------------------------------------------------------------- + | + | Any additional headers you want to send with every request. + | + */ + + 'headers' => [ + 'Accept' => 'application/vnd.api+json', + 'Content-Type' => 'application/vnd.api+json', + ], + +]; diff --git a/example.php b/example.php index 2655e60..c1baf31 100644 --- a/example.php +++ b/example.php @@ -2,10 +2,10 @@ require_once __DIR__ . '/vendor/autoload.php'; -use Timatic\SDK\Timatic; +use Timatic\SDK\TimaticConnector; // Initialize the Timatic SDK -$timatic = new Timatic(); +$timatic = new TimaticConnector(); // Example 1: Get all users echo "Fetching users...\n"; diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php new file mode 100644 index 0000000..e24dc21 --- /dev/null +++ b/generator/JsonApiPestTestGenerator.php @@ -0,0 +1,153 @@ +config->connectorName, $fileStub); + $fileStub = str_replace('{{ namespace }}', $this->config->namespace, $fileStub); + $fileStub = str_replace('{{ name }}', $this->config->connectorName, $fileStub); + $fileStub = str_replace('{{ clientName }}', NameHelper::safeVariableName($this->config->connectorName), $fileStub); + + $namespace = Arr::first($this->generatedCode->connectorClass->getNamespaces()); + $classType = Arr::first($namespace->getClasses()); + + $constructorParameters = $classType->getMethod('__construct')->getParameters(); + $constructorArgs = []; + + foreach ($constructorParameters as $parameter) { + if ($parameter->isNullable()) { + continue; + } + + $defaultValue = match ($parameter->getType()) { + 'string' => "'replace'", + 'bool' => 'true', + 'int' => 0, + default => 'null', + }; + + $constructorArgs[] = $parameter->getName().': '.$defaultValue; + } + + $fileStub = str_replace('{{ connectorArgs }}', Str::wrap(implode(",\n\t\t", $constructorArgs), "\n\t\t", "\n\t"), $fileStub); + + // Generate imports + $imports = []; + foreach ($endpoints as $endpoint) { + $requestClassName = NameHelper::resourceClassName($endpoint->name); + $imports[] = "use {$this->config->namespace}\\{$this->config->requestNamespaceSuffix}\\{$resourceName}\\{$requestClassName};"; + } + + // Add Saloon testing imports + $imports[] = "use Saloon\\Http\\Faking\\MockClient;"; + $imports[] = "use Saloon\\Http\\Faking\\MockResponse;"; + $imports[] = "use Saloon\\Http\\Request;"; + + $fileStub = str_replace('{{ requestImports }}', implode("\n", $imports), $fileStub); + + // Generate DTO imports + $dtoImports = $this->generateDtoImports($endpoints); + $fileStub = str_replace('{{ dtoImports }}', implode("\n", $dtoImports), $fileStub); + + // Generate test functions + foreach ($endpoints as $endpoint) { + $requestClassName = NameHelper::resourceClassName($endpoint->name); + $requestClassNameAlias = $requestClassName == $resourceName ? "{$requestClassName}Request" : null; + + $testFunction = $this->generateTestFunction($endpoint, $resourceName, $requestClassName, $requestClassNameAlias); + $fileStub .= "\n\n{$testFunction}"; + } + + try { + return new TaggedOutputFile( + tag: 'pest', + file: $fileStub, + path: "tests/{$resourceName}Test.php", + ); + } catch (Exception $e) { + return null; + } + } + + protected function generateTestFunction(Endpoint $endpoint, string $resourceName, string $requestClassName, ?string $requestClassNameAlias): string + { + $isMutation = $endpoint->method->isPost() || $endpoint->method->isPatch() || $endpoint->method->isPut(); + + // Use JSON:API stub for mutations, regular stub for others + $stubPath = $isMutation + ? __DIR__.'/stubs/pest-jsonapi-test-func.stub' + : __DIR__.'/../../../saloon-sdk-generator-jsonapi/src/Stubs/pest-resource-test-func.stub'; + + $functionStub = file_get_contents($stubPath); + + $clientName = NameHelper::safeVariableName($this->config->connectorName); + $resourceNameSafe = NameHelper::safeVariableName($resourceName); + $methodNameSafe = NameHelper::safeVariableName($requestClassName); + + $functionStub = str_replace('{{ clientName }}', $clientName, $functionStub); + $functionStub = str_replace('{{ requestClass }}', $requestClassNameAlias ?? $requestClassName, $functionStub); + $functionStub = str_replace('{{ resourceName }}', $resourceNameSafe, $functionStub); + $functionStub = str_replace('{{ methodName }}', $methodNameSafe, $functionStub); + $functionStub = str_replace('{{ fixtureName }}', Str::camel($resourceNameSafe.'.'.$methodNameSafe), $functionStub); + + $description = $isMutation + ? "sends valid JSON:API body when calling {$methodNameSafe} in {$resourceName}" + : "calls the {$methodNameSafe} method in the {$resourceName} resource"; + + $functionStub = str_replace('{{ testDescription }}', $description, $functionStub); + + // Set response status + $responseStatus = $endpoint->method->isPost() ? 201 : 200; + $functionStub = str_replace('{{ responseStatus }}', (string) $responseStatus, $functionStub); + + if ($isMutation) { + // For mutations, create a Model instance + $dtoClassName = NameHelper::dtoClassName($resourceName); + $modelSetup = " \${$resourceNameSafe} = new {$dtoClassName}(['title' => 'Test {$resourceName}']);\n"; + $methodArguments = "\${$resourceNameSafe}"; + + // Add simple body assertion + $bodyAssertions = " // Validate attributes exist\n"; + $bodyAssertions .= " expect(\$data['attributes'])->toBeArray();\n"; + + $functionStub = str_replace('{{ modelSetup }}', $modelSetup, $functionStub); + $functionStub = str_replace('{{ methodArguments }}', $methodArguments, $functionStub); + $functionStub = str_replace('{{ bodyAssertions }}', $bodyAssertions, $functionStub); + } else { + // For non-mutations, use regular fixture-based approach + $methodArguments = []; + foreach ($endpoint->allParameters() as $param) { + $methodArguments[] = sprintf('%s: %s', NameHelper::safeVariableName($param->name), match ($param->type) { + 'string' => "'test string'", + 'int', 'integer' => '123', + default => 'null', + }); + } + + $methodArguments = Str::wrap(implode(",\n\t\t", $methodArguments), "\n\t\t", "\n\t"); + $functionStub = str_replace('{{ methodArguments }}', $methodArguments, $functionStub); + } + + return $functionStub; + } +} diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php new file mode 100644 index 0000000..4abf583 --- /dev/null +++ b/generator/JsonApiRequestGenerator.php @@ -0,0 +1,69 @@ +getNamespaces())[0]; + $classType = array_values($namespace->getClasses())[0]; + + // Add Model import + $namespace->addUse(Model::class); + + // For POST/PUT/PATCH without body parameters, add Model data parameter + if ($this->isMutationRequest($endpoint) && empty($endpoint->bodyParameters)) { + $this->addModelDataParameter($classType, $namespace); + } + + return $phpFile; + } + + protected function isMutationRequest(Endpoint $endpoint): bool + { + return $endpoint->method->isPost() + || $endpoint->method->isPatch() + || $endpoint->method->isPut(); + } + + protected function addModelDataParameter(ClassType $classType, $namespace): void + { + // Get constructor + $constructor = $classType->getMethod('__construct'); + + // Add data parameter with fully qualified type + $constructor->addPromotedParameter('data') + ->setType('\\Timatic\\SDK\\Foundation\\Model|array') + ->setProtected(); + + // Add defaultBody method + $defaultBody = $classType->addMethod('defaultBody') + ->setProtected() + ->setReturnType('array') + ->addBody('return $this->data instanceof Model') + ->addBody(' ? $this->data->toJsonApi()') + ->addBody(' : [\'data\' => $this->data];'); + } +} diff --git a/generator/generate.php b/generator/generate.php index 89e749f..49f15a6 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -8,6 +8,8 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Config; use Crescat\SaloonSdkGenerator\Factory; use Timatic\SDK\Generator\JsonApiDtoGenerator; +use Timatic\SDK\Generator\JsonApiRequestGenerator; +use Timatic\SDK\Generator\JsonApiPestTestGenerator; // Download OpenAPI spec echo "šŸ“„ Downloading OpenAPI specification...\n"; @@ -33,23 +35,28 @@ // Create config $config = new Config( - connectorName: 'Timatic', + connectorName: 'TimaticConnector', namespace: 'Timatic\\SDK', resourceNamespaceSuffix: 'Resource', requestNamespaceSuffix: 'Requests', dtoNamespaceSuffix: 'Dto', ); -// Create code generator with our custom JSON:API DTO generator +// Create code generator with our custom JSON:API generators echo "šŸ—ļø Generating SDK with JSON:API models...\n"; $generator = new CodeGenerator( config: $config, - dtoGenerator: new JsonApiDtoGenerator($config) + dtoGenerator: new JsonApiDtoGenerator($config), + requestGenerator: new JsonApiRequestGenerator($config) ); // Generate the code $result = $generator->run($specification); +// Tests are not generated in this version (requires SDK generator v1.4+) +// You can manually add Pest tests for JSON:API validation +$tests = null; + // Output directory $outputDir = __DIR__ . '/../src'; @@ -97,8 +104,27 @@ function writeFile($file, $outputDir, $namespace) { echo " āœ“ " . basename($path) . "\n"; } +// Write test files +if ($tests && is_array($tests)) { + echo "\n🧪 Tests:\n"; + foreach ($tests as $file) { + if ($file instanceof \Crescat\SaloonSdkGenerator\Data\TaggedOutputFile) { + $testPath = __DIR__ . '/../' . $file->path; + + // Create directory if it doesn't exist + $dir = dirname($testPath); + if (!is_dir($dir)) { + mkdir($dir, 0755, true); + } + + file_put_contents($testPath, $file->file); + echo " āœ“ " . basename($testPath) . "\n"; + } + } +} + echo "\nāœ… SDK generation complete!\n"; echo "\nšŸ’” Next steps:\n"; echo " 1. Run 'composer dump-autoload'\n"; echo " 2. Review generated models in src/Dto/\n"; -echo " 3. Test the SDK\n"; +echo " 3. Run tests with './vendor/bin/pest'\n"; diff --git a/generator/stubs/pest-jsonapi-test-func.stub b/generator/stubs/pest-jsonapi-test-func.stub new file mode 100644 index 0000000..d623413 --- /dev/null +++ b/generator/stubs/pest-jsonapi-test-func.stub @@ -0,0 +1,36 @@ +it('{{ testDescription }}', function () { + $mockClient = new MockClient([ + {{ requestClass }}::class => MockResponse::make(status: {{ responseStatus }}), + ]); + +{{ modelSetup }} + $response = $this->{{ clientName }} + ->withMockClient($mockClient) + ->{{ resourceName }}() + ->{{ methodName }}({{ methodArguments }}); + + // Assert request was sent + $mockClient->assertSent({{ requestClass }}::class); + + // Validate JSON:API body structure + $mockClient->assertSent(function (Request $request) { + $body = $request->body()->all(); + + // Check JSON:API structure + if (!isset($body['data'])) { + return false; + } + + $data = $body['data']; + + // Validate required JSON:API fields + if (!isset($data['type']) || !isset($data['attributes'])) { + return false; + } + +{{ bodyAssertions }} + return true; + }); + + expect($response->status())->toBe({{ responseStatus }}); +}); diff --git a/src/Facades/Timatic.php b/src/Facades/Timatic.php new file mode 100644 index 0000000..7e938fb --- /dev/null +++ b/src/Facades/Timatic.php @@ -0,0 +1,35 @@ +getShortName() )->camel()->plural()->toString(); } + + /** + * Convert Model to JSON:API format + * + * @return array + */ + public function toJsonApi(): array + { + return [ + 'data' => [ + 'type' => $this->type(), + 'attributes' => $this->attributes(), + ], + ]; + } } diff --git a/src/Providers/TimaticServiceProvider.php b/src/Providers/TimaticServiceProvider.php new file mode 100644 index 0000000..526388a --- /dev/null +++ b/src/Providers/TimaticServiceProvider.php @@ -0,0 +1,63 @@ +mergeConfigFrom( + __DIR__.'/../../config/timatic.php', + 'timatic' + ); + + // Register TimaticConnector as singleton + $this->app->singleton(TimaticConnector::class, function ($app) { + $config = $app['config']->get('timatic', []); + + $timatic = new TimaticConnector(); + + // Configure base URL if provided + if (isset($config['base_url'])) { + $timatic->baseUrl($config['base_url']); + } + + // Add default headers if provided + if (isset($config['headers']) && is_array($config['headers'])) { + foreach ($config['headers'] as $key => $value) { + $timatic->headers()->add($key, $value); + } + } + + // Add authentication if provided + if (isset($config['api_token'])) { + $timatic->headers()->add('Authorization', 'Bearer ' . $config['api_token']); + } + + return $timatic; + }); + + // Register alias + $this->app->alias(TimaticConnector::class, 'timatic'); + } + + /** + * Bootstrap services. + */ + public function boot(): void + { + // Publish config + $this->publishes([ + __DIR__.'/../../config/timatic.php' => config_path('timatic.php'), + ], 'timatic-config'); + } +} diff --git a/src/Requests/Approve/PostOvertimeApprove.php b/src/Requests/Approve/PostOvertimeApprove.php index 3bb7c88..a77cdcc 100644 --- a/src/Requests/Approve/PostOvertimeApprove.php +++ b/src/Requests/Approve/PostOvertimeApprove.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postOvertimeApprove @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $overtime, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Budget/DeleteBudget.php b/src/Requests/Budget/DeleteBudget.php index 39e076a..bcf2b08 100644 --- a/src/Requests/Budget/DeleteBudget.php +++ b/src/Requests/Budget/DeleteBudget.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * deleteBudget diff --git a/src/Requests/Budget/GetBudget.php b/src/Requests/Budget/GetBudget.php index 26f1fa8..64ccab0 100644 --- a/src/Requests/Budget/GetBudget.php +++ b/src/Requests/Budget/GetBudget.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudget diff --git a/src/Requests/Budget/GetBudgets.php b/src/Requests/Budget/GetBudgets.php index 54626d9..b244660 100644 --- a/src/Requests/Budget/GetBudgets.php +++ b/src/Requests/Budget/GetBudgets.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudgets diff --git a/src/Requests/Budget/PatchBudget.php b/src/Requests/Budget/PatchBudget.php index 443e208..aa86d28 100644 --- a/src/Requests/Budget/PatchBudget.php +++ b/src/Requests/Budget/PatchBudget.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * patchBudget @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $budget, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Budget/PostBudgets.php b/src/Requests/Budget/PostBudgets.php index cca397c..9806c6c 100644 --- a/src/Requests/Budget/PostBudgets.php +++ b/src/Requests/Budget/PostBudgets.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postBudgets @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/Budget/PutBudget.php b/src/Requests/Budget/PutBudget.php index 6b5d15f..192d516 100644 --- a/src/Requests/Budget/PutBudget.php +++ b/src/Requests/Budget/PutBudget.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * putBudget @@ -25,6 +26,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $budget, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php index 3f2278c..24ea518 100644 --- a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php +++ b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudgetTimeSpentTotals diff --git a/src/Requests/BudgetType/GetBudgetTypes.php b/src/Requests/BudgetType/GetBudgetTypes.php index f165009..83903bb 100644 --- a/src/Requests/BudgetType/GetBudgetTypes.php +++ b/src/Requests/BudgetType/GetBudgetTypes.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudgetTypes diff --git a/src/Requests/Change/GetChange.php b/src/Requests/Change/GetChange.php index c3a6dce..65c7b86 100644 --- a/src/Requests/Change/GetChange.php +++ b/src/Requests/Change/GetChange.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getChange diff --git a/src/Requests/Change/GetChanges.php b/src/Requests/Change/GetChanges.php index 92587c2..892709c 100644 --- a/src/Requests/Change/GetChanges.php +++ b/src/Requests/Change/GetChanges.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getChanges diff --git a/src/Requests/Correction/PatchCorrection.php b/src/Requests/Correction/PatchCorrection.php index c04eed8..daa9ef1 100644 --- a/src/Requests/Correction/PatchCorrection.php +++ b/src/Requests/Correction/PatchCorrection.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * patchCorrection @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $correction, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Correction/PostCorrections.php b/src/Requests/Correction/PostCorrections.php index af6e871..82be959 100644 --- a/src/Requests/Correction/PostCorrections.php +++ b/src/Requests/Correction/PostCorrections.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postCorrections @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/Correction/PutCorrection.php b/src/Requests/Correction/PutCorrection.php index 18cd22c..7c9a5a0 100644 --- a/src/Requests/Correction/PutCorrection.php +++ b/src/Requests/Correction/PutCorrection.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * putCorrection @@ -25,6 +26,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $correction, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Customer/DeleteCustomer.php b/src/Requests/Customer/DeleteCustomer.php index f0a931e..d243be6 100644 --- a/src/Requests/Customer/DeleteCustomer.php +++ b/src/Requests/Customer/DeleteCustomer.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * deleteCustomer diff --git a/src/Requests/Customer/GetCustomer.php b/src/Requests/Customer/GetCustomer.php index 3c0af74..4b1d119 100644 --- a/src/Requests/Customer/GetCustomer.php +++ b/src/Requests/Customer/GetCustomer.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getCustomer diff --git a/src/Requests/Customer/GetCustomers.php b/src/Requests/Customer/GetCustomers.php index 4582b71..e544091 100644 --- a/src/Requests/Customer/GetCustomers.php +++ b/src/Requests/Customer/GetCustomers.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getCustomers diff --git a/src/Requests/Customer/PatchCustomer.php b/src/Requests/Customer/PatchCustomer.php index 1b2de81..0ac17f0 100644 --- a/src/Requests/Customer/PatchCustomer.php +++ b/src/Requests/Customer/PatchCustomer.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * patchCustomer @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $customer, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Customer/PostCustomers.php b/src/Requests/Customer/PostCustomers.php index 2052484..b90a80e 100644 --- a/src/Requests/Customer/PostCustomers.php +++ b/src/Requests/Customer/PostCustomers.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postCustomers @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/Customer/PutCustomer.php b/src/Requests/Customer/PutCustomer.php index aa118e8..746c029 100644 --- a/src/Requests/Customer/PutCustomer.php +++ b/src/Requests/Customer/PutCustomer.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * putCustomer @@ -25,6 +26,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $customer, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/DailyProgress/GetDailyProgresses.php b/src/Requests/DailyProgress/GetDailyProgresses.php index 7596069..0578ec1 100644 --- a/src/Requests/DailyProgress/GetDailyProgresses.php +++ b/src/Requests/DailyProgress/GetDailyProgresses.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getDailyProgresses diff --git a/src/Requests/EntriesExport/GetBudgetEntriesExport.php b/src/Requests/EntriesExport/GetBudgetEntriesExport.php index e33b89b..333c80c 100644 --- a/src/Requests/EntriesExport/GetBudgetEntriesExport.php +++ b/src/Requests/EntriesExport/GetBudgetEntriesExport.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudgetEntriesExport diff --git a/src/Requests/Entry/DeleteEntry.php b/src/Requests/Entry/DeleteEntry.php index fdb2009..d959f21 100644 --- a/src/Requests/Entry/DeleteEntry.php +++ b/src/Requests/Entry/DeleteEntry.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * deleteEntry diff --git a/src/Requests/Entry/GetEntries.php b/src/Requests/Entry/GetEntries.php index b689fcf..708d475 100644 --- a/src/Requests/Entry/GetEntries.php +++ b/src/Requests/Entry/GetEntries.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getEntries diff --git a/src/Requests/Entry/GetEntry.php b/src/Requests/Entry/GetEntry.php index c75ca7b..83e487d 100644 --- a/src/Requests/Entry/GetEntry.php +++ b/src/Requests/Entry/GetEntry.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getEntry diff --git a/src/Requests/Entry/PatchEntry.php b/src/Requests/Entry/PatchEntry.php index 5ffc8b7..bd14f2f 100644 --- a/src/Requests/Entry/PatchEntry.php +++ b/src/Requests/Entry/PatchEntry.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * patchEntry @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $entry, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Entry/PostEntries.php b/src/Requests/Entry/PostEntries.php index e786fa7..650541b 100644 --- a/src/Requests/Entry/PostEntries.php +++ b/src/Requests/Entry/PostEntries.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postEntries @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/Entry/PutEntry.php b/src/Requests/Entry/PutEntry.php index ad1fcbc..06e675b 100644 --- a/src/Requests/Entry/PutEntry.php +++ b/src/Requests/Entry/PutEntry.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * putEntry @@ -25,6 +26,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $entry, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php b/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php index c4632ae..5a4d3dc 100644 --- a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php +++ b/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * deleteEntrySuggestion diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestion.php b/src/Requests/EntrySuggestion/GetEntrySuggestion.php index 73acd2d..db4251a 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestion.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestion.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getEntrySuggestion diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestions.php b/src/Requests/EntrySuggestion/GetEntrySuggestions.php index d51874c..b6d28fe 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestions.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestions.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getEntrySuggestions diff --git a/src/Requests/Event/PostEvents.php b/src/Requests/Event/PostEvents.php index 45c9247..a9ef803 100644 --- a/src/Requests/Event/PostEvents.php +++ b/src/Requests/Event/PostEvents.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postEvents @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/ExportMail/GetBudgetsExportMails.php b/src/Requests/ExportMail/GetBudgetsExportMails.php index e0a323c..d8e896e 100644 --- a/src/Requests/ExportMail/GetBudgetsExportMails.php +++ b/src/Requests/ExportMail/GetBudgetsExportMails.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudgetsExportMails diff --git a/src/Requests/Incident/GetIncident.php b/src/Requests/Incident/GetIncident.php index cb806ca..d71886b 100644 --- a/src/Requests/Incident/GetIncident.php +++ b/src/Requests/Incident/GetIncident.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getIncident diff --git a/src/Requests/Incident/GetIncidents.php b/src/Requests/Incident/GetIncidents.php index 16f4b86..ea1d698 100644 --- a/src/Requests/Incident/GetIncidents.php +++ b/src/Requests/Incident/GetIncidents.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getIncidents diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php index 85d73d9..fa89b17 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postOvertimeMarkAsExported @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $overtime, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php index 1d3ff00..a2d8530 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postEntryMarkAsInvoiced @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $entry, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Me/GetMes.php b/src/Requests/Me/GetMes.php index ee2df76..d46f28f 100644 --- a/src/Requests/Me/GetMes.php +++ b/src/Requests/Me/GetMes.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getMes diff --git a/src/Requests/Number/GetIncidentsNumber.php b/src/Requests/Number/GetIncidentsNumber.php index c8cd2ed..503495c 100644 --- a/src/Requests/Number/GetIncidentsNumber.php +++ b/src/Requests/Number/GetIncidentsNumber.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getIncidentsNumber diff --git a/src/Requests/Overtime/GetOvertimes.php b/src/Requests/Overtime/GetOvertimes.php index 4aff87f..a8dd8f7 100644 --- a/src/Requests/Overtime/GetOvertimes.php +++ b/src/Requests/Overtime/GetOvertimes.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getOvertimes diff --git a/src/Requests/Period/GetBudgetPeriods.php b/src/Requests/Period/GetBudgetPeriods.php index 5ea6a8f..638d6a7 100644 --- a/src/Requests/Period/GetBudgetPeriods.php +++ b/src/Requests/Period/GetBudgetPeriods.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getBudgetPeriods diff --git a/src/Requests/Team/DeleteTeam.php b/src/Requests/Team/DeleteTeam.php index 96a12a5..7db85c6 100644 --- a/src/Requests/Team/DeleteTeam.php +++ b/src/Requests/Team/DeleteTeam.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * deleteTeam diff --git a/src/Requests/Team/GetTeam.php b/src/Requests/Team/GetTeam.php index ca76ca2..012d0d1 100644 --- a/src/Requests/Team/GetTeam.php +++ b/src/Requests/Team/GetTeam.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getTeam diff --git a/src/Requests/Team/GetTeams.php b/src/Requests/Team/GetTeams.php index 56c4a08..555a8d9 100644 --- a/src/Requests/Team/GetTeams.php +++ b/src/Requests/Team/GetTeams.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getTeams diff --git a/src/Requests/Team/PatchTeam.php b/src/Requests/Team/PatchTeam.php index c961109..9607890 100644 --- a/src/Requests/Team/PatchTeam.php +++ b/src/Requests/Team/PatchTeam.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * patchTeam @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $team, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Team/PostTeams.php b/src/Requests/Team/PostTeams.php index 2b359df..63900cf 100644 --- a/src/Requests/Team/PostTeams.php +++ b/src/Requests/Team/PostTeams.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postTeams @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/Team/PutTeam.php b/src/Requests/Team/PutTeam.php index 3bbe593..be53927 100644 --- a/src/Requests/Team/PutTeam.php +++ b/src/Requests/Team/PutTeam.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * putTeam @@ -25,6 +26,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $team, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php b/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php index 64bb3e6..ee326a1 100644 --- a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php +++ b/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getTimeSpentTotals diff --git a/src/Requests/User/DeleteUser.php b/src/Requests/User/DeleteUser.php index d2a17ad..4a253ab 100644 --- a/src/Requests/User/DeleteUser.php +++ b/src/Requests/User/DeleteUser.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * deleteUser diff --git a/src/Requests/User/GetUser.php b/src/Requests/User/GetUser.php index e7f8118..32df144 100644 --- a/src/Requests/User/GetUser.php +++ b/src/Requests/User/GetUser.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getUser diff --git a/src/Requests/User/GetUsers.php b/src/Requests/User/GetUsers.php index b9e3445..12635a6 100644 --- a/src/Requests/User/GetUsers.php +++ b/src/Requests/User/GetUsers.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getUsers diff --git a/src/Requests/User/PatchUser.php b/src/Requests/User/PatchUser.php index 072ea34..bc460d5 100644 --- a/src/Requests/User/PatchUser.php +++ b/src/Requests/User/PatchUser.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * patchUser @@ -29,6 +30,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $user, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/User/PostUsers.php b/src/Requests/User/PostUsers.php index a54a7f3..6749bbf 100644 --- a/src/Requests/User/PostUsers.php +++ b/src/Requests/User/PostUsers.php @@ -7,6 +7,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +use Timatic\SDK\Foundation\Model; /** * postUsers @@ -24,7 +25,16 @@ public function resolveEndpoint(): string } - public function __construct() + public function __construct( + protected Model|array $data, + ) { + } + + + protected function defaultBody(): array { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; } } diff --git a/src/Requests/User/PutUser.php b/src/Requests/User/PutUser.php index 938f587..bab4df7 100644 --- a/src/Requests/User/PutUser.php +++ b/src/Requests/User/PutUser.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * putUser @@ -25,6 +26,15 @@ public function resolveEndpoint(): string */ public function __construct( protected string $user, + protected Model|array $data, ) { } + + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php index 632d4f2..587ae21 100644 --- a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php +++ b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php @@ -5,6 +5,7 @@ use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; +use Timatic\SDK\Foundation\Model; /** * getUserCustomerHoursAggregates diff --git a/src/Timatic.php b/src/TimaticConnector.php similarity index 98% rename from src/Timatic.php rename to src/TimaticConnector.php index c0442f7..872d80f 100644 --- a/src/Timatic.php +++ b/src/TimaticConnector.php @@ -31,7 +31,7 @@ /** * timatic-api */ -class Timatic extends Connector +class TimaticConnector extends Connector { public function __construct() { diff --git a/tests/JsonApiBodyTest.php b/tests/JsonApiBodyTest.php new file mode 100644 index 0000000..3bbe6a5 --- /dev/null +++ b/tests/JsonApiBodyTest.php @@ -0,0 +1,79 @@ + MockResponse::make(status: 201), + ]); + + // Create a Budget model with some data + $budget = new Budget([ + 'title' => 'Q1 2024 Budget', + 'description' => 'First quarter budget', + 'totalPrice' => '10000', + ]); + + // Send the request + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new PostBudgets($budget)); + + // Assert the request was sent + $mockClient->assertSent(PostBudgets::class); + + // Validate JSON:API structure + $mockClient->assertSent(function (Request $request) { + $body = $request->body()->all(); + + // Check for JSON:API 'data' wrapper + if (!isset($body['data'])) { + return false; + } + + $data = $body['data']; + + // Validate required JSON:API fields + if (!isset($data['type'])) { + return false; + } + + if (!isset($data['attributes'])) { + return false; + } + + // Validate that our budget data is in attributes + if ($data['attributes']['title'] !== 'Q1 2024 Budget') { + return false; + } + + if ($data['attributes']['description'] !== 'First quarter budget') { + return false; + } + + return true; + }); + + expect($response->status())->toBe(201); +}); + +it('model has toJsonApi method that returns correct structure', function () { + $budget = new Budget([ + 'title' => 'Test Budget', + 'totalPrice' => '5000', + ]); + + $jsonApi = $budget->toJsonApi(); + + expect($jsonApi)->toHaveKey('data'); + expect($jsonApi['data'])->toHaveKey('type'); + expect($jsonApi['data'])->toHaveKey('attributes'); + expect($jsonApi['data']['type'])->toBe('budgets'); // Pluralized camelCase + expect($jsonApi['data']['attributes'])->toBeArray(); + expect($jsonApi['data']['attributes'])->toHaveKey('title'); + expect($jsonApi['data']['attributes']['title'])->toBe('Test Budget'); +}); diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..d70f61e --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,7 @@ +beforeEach(function () { + $this->timatic = new TimaticConnector(); +})->in(__DIR__); From b1e23e4437c384199c8f71abecec16c25617734e Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 15 Nov 2025 21:49:14 +0100 Subject: [PATCH 03/62] simplified service provider --- README.md | 44 +--- composer.json | 7 +- config/timatic.php | 13 +- src/Providers/TimaticServiceProvider.php | 25 +- src/TimaticConnector.php | 291 +++++++++++------------ tests/Pest.php | 15 +- 6 files changed, 175 insertions(+), 220 deletions(-) diff --git a/README.md b/README.md index 68d0426..1d72040 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ # Timatic PHP SDK -A PHP SDK for the Timatic API, built with [Saloon](https://docs.saloon.dev/) and automatically generated from OpenAPI specifications. +A Laravel package for the Timatic API, built with [Saloon](https://docs.saloon.dev/) and automatically generated from OpenAPI specifications. + +## Requirements + +- PHP 8.1 or higher +- Laravel 10.x or higher ## Installation @@ -8,41 +13,10 @@ A PHP SDK for the Timatic API, built with [Saloon](https://docs.saloon.dev/) and composer require timatic/php-sdk ``` -## Usage - -### Standalone PHP - -```php -use Timatic\SDK\TimaticConnector; - -// Initialize the SDK -$timatic = new TimaticConnector(); - -// Example: Get users -$response = $timatic->user()->getUsers(); -$users = $response->json(); - -// Example: Get a specific customer -$response = $timatic->customer()->getCustomer(id: 1); -$customer = $response->json(); - -// Example: Create a new budget with a Model -use Timatic\SDK\Dto\Budget; - -$budget = new Budget([ - 'title' => 'Q1 2024 Budget', - 'totalPrice' => '50000', -]); - -$response = $timatic->send(new \Timatic\SDK\Requests\Budget\PostBudgets($budget)); -``` - -### Laravel +## Configuration The package automatically registers itself via Laravel auto-discovery. -#### Configuration - Publish the config file: ```bash @@ -56,7 +30,9 @@ TIMATIC_BASE_URL=https://api.app.timatic.test TIMATIC_API_TOKEN=your-api-token-here ``` -#### Using Dependency Injection (Recommended) +## Usage + +### Using Dependency Injection The SDK connector is automatically registered in Laravel's service container, making it easy to inject into your controllers, commands, and other classes: diff --git a/composer.json b/composer.json index 3285835..14983f8 100644 --- a/composer.json +++ b/composer.json @@ -18,13 +18,16 @@ "php": "^8.1", "saloonphp/saloon": "^3.0", "nesbot/carbon": "^2.0|^3.0", - "illuminate/support": "^10.0|^11.0" + "illuminate/support": "^10.0", + "saloonphp/pagination-plugin": "^2.0" }, "require-dev": { "laravel/boost": "^1.8", "phpunit/phpunit": "^10.0|^11.0", "pestphp/pest": "^2.0|^3.0", - "crescat-io/saloon-sdk-generator": "^1.0" + "crescat-io/saloon-sdk-generator": "^1.0", + "laravel/pint": "^1.0", + "larastan/larastan": "^2.0" }, "scripts": { "test": "pest" diff --git a/config/timatic.php b/config/timatic.php index 0fd2a7b..16dfc02 100644 --- a/config/timatic.php +++ b/config/timatic.php @@ -31,13 +31,14 @@ | Default Headers |-------------------------------------------------------------------------- | - | Any additional headers you want to send with every request. + | The SDK automatically includes the following headers: + | - Accept: application/vnd.api+json + | - Content-Type: application/vnd.api+json + | - Authorization: Bearer {api_token} (if api_token is set) + | + | This configuration is reserved for future use if you need to add + | additional custom headers beyond the defaults. | */ - 'headers' => [ - 'Accept' => 'application/vnd.api+json', - 'Content-Type' => 'application/vnd.api+json', - ], - ]; diff --git a/src/Providers/TimaticServiceProvider.php b/src/Providers/TimaticServiceProvider.php index 526388a..5394521 100644 --- a/src/Providers/TimaticServiceProvider.php +++ b/src/Providers/TimaticServiceProvider.php @@ -21,30 +21,7 @@ public function register(): void ); // Register TimaticConnector as singleton - $this->app->singleton(TimaticConnector::class, function ($app) { - $config = $app['config']->get('timatic', []); - - $timatic = new TimaticConnector(); - - // Configure base URL if provided - if (isset($config['base_url'])) { - $timatic->baseUrl($config['base_url']); - } - - // Add default headers if provided - if (isset($config['headers']) && is_array($config['headers'])) { - foreach ($config['headers'] as $key => $value) { - $timatic->headers()->add($key, $value); - } - } - - // Add authentication if provided - if (isset($config['api_token'])) { - $timatic->headers()->add('Authorization', 'Bearer ' . $config['api_token']); - } - - return $timatic; - }); + $this->app->singleton(TimaticConnector::class); // Register alias $this->app->alias(TimaticConnector::class, 'timatic'); diff --git a/src/TimaticConnector.php b/src/TimaticConnector.php index 872d80f..6593634 100644 --- a/src/TimaticConnector.php +++ b/src/TimaticConnector.php @@ -33,157 +33,142 @@ */ class TimaticConnector extends Connector { - public function __construct() - { - } - - - public function resolveBaseUrl(): string - { - return "https://api.app.timatic.test"; - } - - - public function approve(): Approve - { - return new Approve($this); - } - - - public function budget(): Budget - { - return new Budget($this); - } - - - public function budgetTimeSpentTotal(): BudgetTimeSpentTotal - { - return new BudgetTimeSpentTotal($this); - } - - - public function budgetType(): BudgetType - { - return new BudgetType($this); - } - - - public function change(): Change - { - return new Change($this); - } - - - public function correction(): Correction - { - return new Correction($this); - } - - - public function customer(): Customer - { - return new Customer($this); - } - - - public function dailyProgress(): DailyProgress - { - return new DailyProgress($this); - } - - - public function entriesExport(): EntriesExport - { - return new EntriesExport($this); - } - - - public function entry(): Entry - { - return new Entry($this); - } - - - public function entrySuggestion(): EntrySuggestion - { - return new EntrySuggestion($this); - } - - - public function event(): Event - { - return new Event($this); - } - - - public function exportMail(): ExportMail - { - return new ExportMail($this); - } - - - public function incident(): Incident - { - return new Incident($this); - } - - - public function markAsExported(): MarkAsExported - { - return new MarkAsExported($this); - } - - - public function markAsInvoiced(): MarkAsInvoiced - { - return new MarkAsInvoiced($this); - } - - - public function me(): Me - { - return new Me($this); - } - - - public function number(): Number - { - return new Number($this); - } - - - public function overtime(): Overtime - { - return new Overtime($this); - } - - - public function period(): Period - { - return new Period($this); - } - - - public function team(): Team - { - return new Team($this); - } - - - public function timeSpentTotal(): TimeSpentTotal - { - return new TimeSpentTotal($this); - } - - - public function user(): User - { - return new User($this); - } - - - public function userCustomerHoursAggregate(): UserCustomerHoursAggregate - { - return new UserCustomerHoursAggregate($this); - } + public function resolveBaseUrl(): string + { + return config('timatic.base_url'); + } + + protected function defaultHeaders(): array + { + $headers = [ + 'Accept' => 'application/vnd.api+json', + 'Content-Type' => 'application/vnd.api+json', + ]; + + if ($token = config('timatic.api_token')) { + $headers['Authorization'] = "Bearer {$token}"; + } + + return $headers; + } + + public function approve(): Approve + { + return new Approve($this); + } + + public function budget(): Budget + { + return new Budget($this); + } + + public function budgetTimeSpentTotal(): BudgetTimeSpentTotal + { + return new BudgetTimeSpentTotal($this); + } + + public function budgetType(): BudgetType + { + return new BudgetType($this); + } + + public function change(): Change + { + return new Change($this); + } + + public function correction(): Correction + { + return new Correction($this); + } + + public function customer(): Customer + { + return new Customer($this); + } + + public function dailyProgress(): DailyProgress + { + return new DailyProgress($this); + } + + public function entriesExport(): EntriesExport + { + return new EntriesExport($this); + } + + public function entry(): Entry + { + return new Entry($this); + } + + public function entrySuggestion(): EntrySuggestion + { + return new EntrySuggestion($this); + } + + public function event(): Event + { + return new Event($this); + } + + public function exportMail(): ExportMail + { + return new ExportMail($this); + } + + public function incident(): Incident + { + return new Incident($this); + } + + public function markAsExported(): MarkAsExported + { + return new MarkAsExported($this); + } + + public function markAsInvoiced(): MarkAsInvoiced + { + return new MarkAsInvoiced($this); + } + + public function me(): Me + { + return new Me($this); + } + + public function number(): Number + { + return new Number($this); + } + + public function overtime(): Overtime + { + return new Overtime($this); + } + + public function period(): Period + { + return new Period($this); + } + + public function team(): Team + { + return new Team($this); + } + + public function timeSpentTotal(): TimeSpentTotal + { + return new TimeSpentTotal($this); + } + + public function user(): User + { + return new User($this); + } + + public function userCustomerHoursAggregate(): UserCustomerHoursAggregate + { + return new UserCustomerHoursAggregate($this); + } } diff --git a/tests/Pest.php b/tests/Pest.php index d70f61e..c2e8ade 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -2,6 +2,19 @@ use Timatic\SDK\TimaticConnector; +// Mock config helper for tests +if (! function_exists('config')) { + function config(string $key, $default = null) + { + $config = [ + 'timatic.base_url' => 'https://api.app.timatic.test', + 'timatic.api_token' => null, + ]; + + return $config[$key] ?? $default; + } +} + uses()->beforeEach(function () { - $this->timatic = new TimaticConnector(); + $this->timatic = new TimaticConnector; })->in(__DIR__); From 55056388686c6c88e481c32b0d7ee75aef2bfd4a Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 15 Nov 2025 21:53:04 +0100 Subject: [PATCH 04/62] Response class --- src/Responses/TimaticResponse.php | 64 +++++++++++++++++++++++++++++++ src/TimaticConnector.php | 6 +++ 2 files changed, 70 insertions(+) create mode 100644 src/Responses/TimaticResponse.php diff --git a/src/Responses/TimaticResponse.php b/src/Responses/TimaticResponse.php new file mode 100644 index 0000000..1d26ae9 --- /dev/null +++ b/src/Responses/TimaticResponse.php @@ -0,0 +1,64 @@ +json('data'); + + if (is_array($data) && isset($data[0])) { + return $data[0]; + } + + return $data; + } + + /** + * Check if response contains JSON:API errors + */ + public function hasErrors(): bool + { + return $this->json('errors') !== null; + } + + /** + * Get JSON:API errors + */ + public function errors(): array + { + return $this->json('errors', []); + } + + /** + * Get meta information from JSON:API response + */ + public function meta(): array + { + return $this->json('meta', []); + } + + /** + * Get links from JSON:API response + */ + public function links(): array + { + return $this->json('links', []); + } + + /** + * Get included resources from JSON:API response + */ + public function included(): array + { + return $this->json('included', []); + } +} diff --git a/src/TimaticConnector.php b/src/TimaticConnector.php index 6593634..3c386c9 100644 --- a/src/TimaticConnector.php +++ b/src/TimaticConnector.php @@ -27,6 +27,7 @@ use Timatic\SDK\Resource\TimeSpentTotal; use Timatic\SDK\Resource\User; use Timatic\SDK\Resource\UserCustomerHoursAggregate; +use Timatic\SDK\Responses\TimaticResponse; /** * timatic-api @@ -52,6 +53,11 @@ protected function defaultHeaders(): array return $headers; } + public function resolveResponseClass(): string + { + return TimaticResponse::class; + } + public function approve(): Approve { return new Approve($this); From 1e081c3248c4aac7a64784538a8f908070a3af81 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 15 Nov 2025 22:06:30 +0100 Subject: [PATCH 05/62] pagination --- generator/JsonApiRequestGenerator.php | 28 ++++-- src/Pagination/JsonApiPaginator.php | 33 +++++++ src/Requests/Budget/GetBudgets.php | 135 +++++++++++--------------- src/TimaticConnector.php | 10 +- tests/PaginationTest.php | 112 +++++++++++++++++++++ 5 files changed, 227 insertions(+), 91 deletions(-) create mode 100644 src/Pagination/JsonApiPaginator.php create mode 100644 tests/PaginationTest.php diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 4abf583..add11f0 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -4,19 +4,11 @@ namespace Timatic\SDK\Generator; -use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; -use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use DateTime; -use Illuminate\Support\Str; use Nette\PhpGenerator\ClassType; -use Nette\PhpGenerator\Literal; use Nette\PhpGenerator\PhpFile; -use Saloon\Contracts\Body\HasBody; -use Saloon\Enums\Method as SaloonHttpMethod; -use Saloon\Http\Request; -use Saloon\Traits\Body\HasJsonBody; +use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Foundation\Model; class JsonApiRequestGenerator extends RequestGenerator @@ -38,6 +30,12 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile $this->addModelDataParameter($classType, $namespace); } + // For GET collection requests, add Paginatable interface + if ($this->isCollectionRequest($endpoint)) { + $namespace->addUse(Paginatable::class); + $classType->addImplement(Paginatable::class); + } + return $phpFile; } @@ -48,6 +46,18 @@ protected function isMutationRequest(Endpoint $endpoint): bool || $endpoint->method->isPut(); } + protected function isCollectionRequest(Endpoint $endpoint): bool + { + // Collection requests are GET requests without an ID parameter in the path + if (! $endpoint->method->isGet()) { + return false; + } + + // Check if the path contains a parameter (like {id}, {budget}, etc.) + // Collection endpoints typically don't have path parameters + return empty($endpoint->pathParameters); + } + protected function addModelDataParameter(ClassType $classType, $namespace): void { // Get constructor diff --git a/src/Pagination/JsonApiPaginator.php b/src/Pagination/JsonApiPaginator.php new file mode 100644 index 0000000..60c1b20 --- /dev/null +++ b/src/Pagination/JsonApiPaginator.php @@ -0,0 +1,33 @@ +json('links.next') === null; + } + + protected function getPageItems(Response $response, Request $request): array + { + return $response->dto()->toArray(); + } + + protected function applyPagination(Request $request): Request + { + $request->query()->add('page[number]', $this->page); + + if (isset($this->perPageLimit)) { + $request->query()->add('page[size]', $this->perPageLimit); + } + + return $request; + } +} diff --git a/src/Requests/Budget/GetBudgets.php b/src/Requests/Budget/GetBudgets.php index b244660..df0023f 100644 --- a/src/Requests/Budget/GetBudgets.php +++ b/src/Requests/Budget/GetBudgets.php @@ -2,95 +2,68 @@ namespace Timatic\SDK\Requests\Budget; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getBudgets */ -class GetBudgets extends Request +class GetBudgets extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/budgets'; + } - public function resolveEndpoint(): string - { - return "/budgets"; - } + public function __construct( + protected ?int $filtercustomerId = null, + protected ?int $filtercustomerIdeq = null, + protected ?int $filtercustomerIdnq = null, + protected ?int $filtercustomerIdgt = null, + protected ?int $filtercustomerIdlt = null, + protected ?int $filtercustomerIdgte = null, + protected ?int $filtercustomerIdlte = null, + protected ?int $filtercustomerIdcontains = null, + protected ?string $filterbudgetTypeId = null, + protected ?string $filterbudgetTypeIdeq = null, + protected ?string $filterbudgetTypeIdnq = null, + protected ?string $filterbudgetTypeIdgt = null, + protected ?string $filterbudgetTypeIdlt = null, + protected ?string $filterbudgetTypeIdgte = null, + protected ?string $filterbudgetTypeIdlte = null, + protected ?string $filterbudgetTypeIdcontains = null, + protected ?string $filterisArchived = null, + protected ?string $filtercustomerExternalId = null, + protected ?string $filtershowToCustomer = null, + protected ?string $include = null, + ) {} - - /** - * @param null|int $filtercustomerId - * @param null|int $filtercustomerIdeq - * @param null|int $filtercustomerIdnq - * @param null|int $filtercustomerIdgt - * @param null|int $filtercustomerIdlt - * @param null|int $filtercustomerIdgte - * @param null|int $filtercustomerIdlte - * @param null|int $filtercustomerIdcontains - * @param null|string $filterbudgetTypeId - * @param null|string $filterbudgetTypeIdeq - * @param null|string $filterbudgetTypeIdnq - * @param null|string $filterbudgetTypeIdgt - * @param null|string $filterbudgetTypeIdlt - * @param null|string $filterbudgetTypeIdgte - * @param null|string $filterbudgetTypeIdlte - * @param null|string $filterbudgetTypeIdcontains - * @param null|string $filterisArchived - * @param null|string $filtercustomerExternalId - * @param null|string $filtershowToCustomer - * @param null|string $include - */ - public function __construct( - protected ?int $filtercustomerId = null, - protected ?int $filtercustomerIdeq = null, - protected ?int $filtercustomerIdnq = null, - protected ?int $filtercustomerIdgt = null, - protected ?int $filtercustomerIdlt = null, - protected ?int $filtercustomerIdgte = null, - protected ?int $filtercustomerIdlte = null, - protected ?int $filtercustomerIdcontains = null, - protected ?string $filterbudgetTypeId = null, - protected ?string $filterbudgetTypeIdeq = null, - protected ?string $filterbudgetTypeIdnq = null, - protected ?string $filterbudgetTypeIdgt = null, - protected ?string $filterbudgetTypeIdlt = null, - protected ?string $filterbudgetTypeIdgte = null, - protected ?string $filterbudgetTypeIdlte = null, - protected ?string $filterbudgetTypeIdcontains = null, - protected ?string $filterisArchived = null, - protected ?string $filtercustomerExternalId = null, - protected ?string $filtershowToCustomer = null, - protected ?string $include = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[customerId]' => $this->filtercustomerId, - 'filter[customerId][eq]' => $this->filtercustomerIdeq, - 'filter[customerId][nq]' => $this->filtercustomerIdnq, - 'filter[customerId][gt]' => $this->filtercustomerIdgt, - 'filter[customerId][lt]' => $this->filtercustomerIdlt, - 'filter[customerId][gte]' => $this->filtercustomerIdgte, - 'filter[customerId][lte]' => $this->filtercustomerIdlte, - 'filter[customerId][contains]' => $this->filtercustomerIdcontains, - 'filter[budgetTypeId]' => $this->filterbudgetTypeId, - 'filter[budgetTypeId][eq]' => $this->filterbudgetTypeIdeq, - 'filter[budgetTypeId][nq]' => $this->filterbudgetTypeIdnq, - 'filter[budgetTypeId][gt]' => $this->filterbudgetTypeIdgt, - 'filter[budgetTypeId][lt]' => $this->filterbudgetTypeIdlt, - 'filter[budgetTypeId][gte]' => $this->filterbudgetTypeIdgte, - 'filter[budgetTypeId][lte]' => $this->filterbudgetTypeIdlte, - 'filter[budgetTypeId][contains]' => $this->filterbudgetTypeIdcontains, - 'filter[isArchived]' => $this->filterisArchived, - 'filter[customerExternalId]' => $this->filtercustomerExternalId, - 'filter[showToCustomer]' => $this->filtershowToCustomer, - 'include' => $this->include, - ]); - } + public function defaultQuery(): array + { + return array_filter([ + 'filter[customerId]' => $this->filtercustomerId, + 'filter[customerId][eq]' => $this->filtercustomerIdeq, + 'filter[customerId][nq]' => $this->filtercustomerIdnq, + 'filter[customerId][gt]' => $this->filtercustomerIdgt, + 'filter[customerId][lt]' => $this->filtercustomerIdlt, + 'filter[customerId][gte]' => $this->filtercustomerIdgte, + 'filter[customerId][lte]' => $this->filtercustomerIdlte, + 'filter[customerId][contains]' => $this->filtercustomerIdcontains, + 'filter[budgetTypeId]' => $this->filterbudgetTypeId, + 'filter[budgetTypeId][eq]' => $this->filterbudgetTypeIdeq, + 'filter[budgetTypeId][nq]' => $this->filterbudgetTypeIdnq, + 'filter[budgetTypeId][gt]' => $this->filterbudgetTypeIdgt, + 'filter[budgetTypeId][lt]' => $this->filterbudgetTypeIdlt, + 'filter[budgetTypeId][gte]' => $this->filterbudgetTypeIdgte, + 'filter[budgetTypeId][lte]' => $this->filterbudgetTypeIdlte, + 'filter[budgetTypeId][contains]' => $this->filterbudgetTypeIdcontains, + 'filter[isArchived]' => $this->filterisArchived, + 'filter[customerExternalId]' => $this->filtercustomerExternalId, + 'filter[showToCustomer]' => $this->filtershowToCustomer, + 'include' => $this->include, + ]); + } } diff --git a/src/TimaticConnector.php b/src/TimaticConnector.php index 3c386c9..d290430 100644 --- a/src/TimaticConnector.php +++ b/src/TimaticConnector.php @@ -3,6 +3,9 @@ namespace Timatic\SDK; use Saloon\Http\Connector; +use Saloon\Http\Request; +use Saloon\PaginationPlugin\Contracts\HasPagination; +use Timatic\SDK\Pagination\JsonApiPaginator; use Timatic\SDK\Resource\Approve; use Timatic\SDK\Resource\Budget; use Timatic\SDK\Resource\BudgetTimeSpentTotal; @@ -32,7 +35,7 @@ /** * timatic-api */ -class TimaticConnector extends Connector +class TimaticConnector extends Connector implements HasPagination { public function resolveBaseUrl(): string { @@ -58,6 +61,11 @@ public function resolveResponseClass(): string return TimaticResponse::class; } + public function paginate(Request $request): JsonApiPaginator + { + return new JsonApiPaginator($this, $request); + } + public function approve(): Approve { return new Approve($this); diff --git a/tests/PaginationTest.php b/tests/PaginationTest.php new file mode 100644 index 0000000..1de7628 --- /dev/null +++ b/tests/PaginationTest.php @@ -0,0 +1,112 @@ + [ + ['type' => 'budgets', 'id' => '1', 'attributes' => ['title' => 'Budget 1']], + ['type' => 'budgets', 'id' => '2', 'attributes' => ['title' => 'Budget 2']], + ], + 'links' => [ + 'next' => 'https://api.app.timatic.test/budgets?page[number]=2&page[size]=2', + 'first' => 'https://api.app.timatic.test/budgets?page[number]=1&page[size]=2', + ], + ]), + // Page 2 - has next link + MockResponse::make([ + 'data' => [ + ['type' => 'budgets', 'id' => '3', 'attributes' => ['title' => 'Budget 3']], + ['type' => 'budgets', 'id' => '4', 'attributes' => ['title' => 'Budget 4']], + ], + 'links' => [ + 'next' => 'https://api.app.timatic.test/budgets?page[number]=3&page[size]=2', + 'prev' => 'https://api.app.timatic.test/budgets?page[number]=1&page[size]=2', + ], + ]), + // Page 3 - last page, no next link + MockResponse::make([ + 'data' => [ + ['type' => 'budgets', 'id' => '5', 'attributes' => ['title' => 'Budget 5']], + ], + 'links' => [ + 'next' => null, + 'prev' => 'https://api.app.timatic.test/budgets?page[number]=2&page[size]=2', + ], + ]), + ]); + + $timatic = $this->timatic->withMockClient($mockClient); + + // Create paginator + $paginator = $timatic->paginate(new GetBudgets); + $paginator->setPerPageLimit(2); + + // Collect all items + $allBudgets = []; + foreach ($paginator->items() as $budget) { + $allBudgets[] = $budget; + } + + // Should have collected 5 budgets total across 3 pages + expect($allBudgets)->toHaveCount(5); + + // Verify all 3 requests were made + expect($mockClient->getRecordedRequests())->toHaveCount(3); + + // Verify first request has correct pagination params + $firstRequest = $mockClient->getRecordedRequests()[0]; + expect($firstRequest->query()->get('page[number]'))->toBe(1); + expect($firstRequest->query()->get('page[size]'))->toBe(2); + + // Verify second request incremented page number + $secondRequest = $mockClient->getRecordedRequests()[1]; + expect($secondRequest->query()->get('page[number]'))->toBe(2); + expect($secondRequest->query()->get('page[size]'))->toBe(2); + + // Verify third request + $thirdRequest = $mockClient->getRecordedRequests()[2]; + expect($thirdRequest->query()->get('page[number]'))->toBe(3); +}); + +it('stops pagination when links.next is null', function () { + $mockClient = new MockClient([ + // Single page - no next link + MockResponse::make([ + 'data' => [ + ['type' => 'budgets', 'id' => '1', 'attributes' => ['title' => 'Budget 1']], + ], + 'links' => [ + 'next' => null, + 'first' => 'https://api.app.timatic.test/budgets?page[number]=1', + ], + ]), + ]); + + $timatic = $this->timatic->withMockClient($mockClient); + $paginator = $timatic->paginate(new GetBudgets); + + $allBudgets = []; + foreach ($paginator->items() as $budget) { + $allBudgets[] = $budget; + } + + // Should only have 1 budget + expect($allBudgets)->toHaveCount(1); + + // Should only have made 1 request + expect($mockClient->getRecordedRequests())->toHaveCount(1); +}); + +it('request implements Paginatable interface', function () { + $request = new GetBudgets; + + expect($request)->toBeInstanceOf(Paginatable::class); +}); From 0b140d938f5d760b1c66d17bfc1473e2c4e80315 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 08:09:11 +0100 Subject: [PATCH 06/62] fixes --- CLAUDE.md | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++- composer.json | 1 - example.php | 19 ++++++------ 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 099a2d2..49ab2a2 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -252,4 +252,82 @@ protected function isAccessible(User $user, ?string $path = null): bool ## Tailwind 3 - Always use Tailwind CSS v3 - verify you're using only classes supported by this version. - \ No newline at end of file + + +=== timatic-sdk rules === + +## Timatic PHP SDK - Auto-Generated Files + +This SDK is automatically generated from the OpenAPI specification. **DO NOT manually edit auto-generated files**. + +### āŒ NEVER EDIT THESE DIRECTORIES: +- **`src/Dto/`** - All Model/DTO classes (auto-generated with flattened JSON:API attributes) +- **`src/Requests/`** - All Request classes (auto-generated from OpenAPI endpoints) +- **`src/Resource/`** - All Resource classes (auto-generated to group related requests) + +### āš ļø SPECIAL CASE - Connector File: +- **`src/TimaticConnector.php`** - Partially auto-generated. The resource methods are auto-generated, but custom configuration methods MUST be preserved: + - `resolveBaseUrl()` + - `defaultHeaders()` + - `resolveResponseClass()` + - `paginate()` + + **After SDK regeneration, you MUST restore this file:** + ```bash + git checkout src/TimaticConnector.php + ``` + +### āœ… SAFE TO EDIT: +- **`src/Foundation/`** - Base classes (Model, HasAttributes, etc.) +- **`src/Attributes/`** - PHP attributes (Property, DateTime) +- **`src/Responses/`** - Custom response classes (TimaticResponse) +- **`src/Pagination/`** - Pagination classes (JsonApiPaginator) +- **`src/Providers/`** - Laravel service providers +- **`src/Facades/`** - Laravel facades +- **`config/timatic.php`** - Configuration file +- **`tests/`** - Test files +- **`generator/`** - Generator classes + +## SDK Regeneration Commands + +To update auto-generated files from the latest OpenAPI spec: + +```bash +# 1. Run the generator +php generator/generate.php + +# 2. IMMEDIATELY restore TimaticConnector (contains custom methods) +git checkout src/TimaticConnector.php + +# 3. Update autoloader +composer dump-autoload + +# 4. Run tests to verify +./vendor/bin/pest + +# 5. Format code +./vendor/bin/pint +``` + +## Modifying Auto-Generated Code + +If you need to change how files are generated, update the generators: + +- **`generator/JsonApiDtoGenerator.php`** - Customizes Model/DTO generation + - Flattens JSON:API attributes into typed properties + - Adds `#[Property]` and `#[DateTime]` attributes + +- **`generator/JsonApiRequestGenerator.php`** - Customizes Request generation + - Adds Model support for POST/PUT/PATCH + - Adds `Paginatable` interface to GET collection requests + - Generates `defaultBody()` method for JSON:API serialization + +- **`generator/generate.php`** - Main generation script + +After modifying generators, regenerate and test: +```bash +php generator/generate.php +git checkout src/TimaticConnector.php +composer dump-autoload +./vendor/bin/pest +``` \ No newline at end of file diff --git a/composer.json b/composer.json index 14983f8..28c35ac 100644 --- a/composer.json +++ b/composer.json @@ -25,7 +25,6 @@ "laravel/boost": "^1.8", "phpunit/phpunit": "^10.0|^11.0", "pestphp/pest": "^2.0|^3.0", - "crescat-io/saloon-sdk-generator": "^1.0", "laravel/pint": "^1.0", "larastan/larastan": "^2.0" }, diff --git a/example.php b/example.php index c1baf31..14add1e 100644 --- a/example.php +++ b/example.php @@ -1,18 +1,18 @@ user()->getUsers(); if ($response->successful()) { $users = $response->json(); - echo "Found " . count($users['data'] ?? []) . " users\n\n"; + echo 'Found '.count($users['data'] ?? [])." users\n\n"; } // Example 2: Get all customers @@ -20,7 +20,7 @@ $response = $timatic->customer()->getCustomers(); if ($response->successful()) { $customers = $response->json(); - echo "Found " . count($customers['data'] ?? []) . " customers\n\n"; + echo 'Found '.count($customers['data'] ?? [])." customers\n\n"; } // Example 3: Get budgets @@ -28,7 +28,7 @@ $response = $timatic->budget()->getBudgets(); if ($response->successful()) { $budgets = $response->json(); - echo "Found " . count($budgets['data'] ?? []) . " budgets\n\n"; + echo 'Found '.count($budgets['data'] ?? [])." budgets\n\n"; } // Example 4: Get current user info @@ -36,25 +36,24 @@ $response = $timatic->me()->getMes(); if ($response->successful()) { $me = $response->json(); - echo "Current user: " . ($me['data']['name'] ?? 'Unknown') . "\n\n"; + echo 'Current user: '.($me['data']['name'] ?? 'Unknown')."\n\n"; } // Example 5: Create a new entry (commented out to prevent accidental execution) -/* + echo "Creating a new entry...\n"; -$response = $timatic->entry()->postEntries([ +$response = $timatic->entry()->postEntries(new \Timatic\SDK\Dto\Entry([ 'user_id' => 1, 'customer_id' => 1, 'date' => date('Y-m-d'), 'hours' => 2.5, 'description' => 'Working on SDK integration', -]); +])); if ($response->successful()) { echo "Entry created successfully!\n"; $entry = $response->json(); var_dump($entry); } -*/ echo "Examples completed!\n"; From 4e96dee91ae6d03ee9bce5bd930ecb17bed29fe4 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 08:09:57 +0100 Subject: [PATCH 07/62] tests --- src/Pagination/JsonApiPaginator.php | 3 +- tests/PaginationTest.php | 2 +- tests/TimaticResponseTest.php | 215 ++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 2 deletions(-) create mode 100644 tests/TimaticResponseTest.php diff --git a/src/Pagination/JsonApiPaginator.php b/src/Pagination/JsonApiPaginator.php index 60c1b20..f266876 100644 --- a/src/Pagination/JsonApiPaginator.php +++ b/src/Pagination/JsonApiPaginator.php @@ -17,7 +17,8 @@ protected function isLastPage(Response $response): bool protected function getPageItems(Response $response, Request $request): array { - return $response->dto()->toArray(); + // Return the 'data' array from JSON:API response + return $response->json('data', []); } protected function applyPagination(Request $request): Request diff --git a/tests/PaginationTest.php b/tests/PaginationTest.php index 1de7628..62c3007 100644 --- a/tests/PaginationTest.php +++ b/tests/PaginationTest.php @@ -102,7 +102,7 @@ expect($allBudgets)->toHaveCount(1); // Should only have made 1 request - expect($mockClient->getRecordedRequests())->toHaveCount(1); + $mockClient->assertSentCount(1); }); it('request implements Paginatable interface', function () { diff --git a/tests/TimaticResponseTest.php b/tests/TimaticResponseTest.php new file mode 100644 index 0000000..c66f6e6 --- /dev/null +++ b/tests/TimaticResponseTest.php @@ -0,0 +1,215 @@ + [ + ['type' => 'budgets', 'id' => '1', 'attributes' => ['title' => 'Budget 1']], + ['type' => 'budgets', 'id' => '2', 'attributes' => ['title' => 'Budget 2']], + ], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response)->toBeInstanceOf(TimaticResponse::class); + + $firstItem = $response->firstItem(); + expect($firstItem)->toBeArray(); + expect($firstItem['id'])->toBe('1'); + expect($firstItem['attributes']['title'])->toBe('Budget 1'); +}); + +it('returns single item for non-collection response', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [ + 'type' => 'budgets', + 'id' => '1', + 'attributes' => ['title' => 'Budget 1'], + ], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + $firstItem = $response->firstItem(); + expect($firstItem)->toBeArray(); + expect($firstItem['id'])->toBe('1'); + expect($firstItem['attributes']['title'])->toBe('Budget 1'); +}); + +it('detects errors in response', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'errors' => [ + [ + 'status' => '404', + 'title' => 'Not Found', + 'detail' => 'Budget not found', + ], + ], + ], 404), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->hasErrors())->toBeTrue(); + expect($response->errors())->toBeArray(); + expect($response->errors())->toHaveCount(1); + expect($response->errors()[0]['status'])->toBe('404'); + expect($response->errors()[0]['title'])->toBe('Not Found'); +}); + +it('returns empty array when no errors exist', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [ + ['type' => 'budgets', 'id' => '1'], + ], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->hasErrors())->toBeFalse(); + expect($response->errors())->toBeArray(); + expect($response->errors())->toHaveCount(0); +}); + +it('returns meta information from response', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [], + 'meta' => [ + 'total' => 100, + 'page' => 1, + 'perPage' => 20, + ], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->meta())->toBeArray(); + expect($response->meta()['total'])->toBe(100); + expect($response->meta()['page'])->toBe(1); + expect($response->meta()['perPage'])->toBe(20); +}); + +it('returns empty array when no meta exists', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->meta())->toBeArray(); + expect($response->meta())->toHaveCount(0); +}); + +it('returns links from response', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [], + 'links' => [ + 'self' => 'https://api.app.timatic.test/budgets', + 'first' => 'https://api.app.timatic.test/budgets?page[number]=1', + 'next' => 'https://api.app.timatic.test/budgets?page[number]=2', + ], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->links())->toBeArray(); + expect($response->links()['self'])->toBe('https://api.app.timatic.test/budgets'); + expect($response->links()['next'])->toBe('https://api.app.timatic.test/budgets?page[number]=2'); +}); + +it('returns empty array when no links exist', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->links())->toBeArray(); + expect($response->links())->toHaveCount(0); +}); + +it('returns included resources from response', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [ + [ + 'type' => 'budgets', + 'id' => '1', + 'attributes' => ['title' => 'Budget 1'], + 'relationships' => [ + 'customer' => [ + 'data' => ['type' => 'customers', 'id' => '10'], + ], + ], + ], + ], + 'included' => [ + [ + 'type' => 'customers', + 'id' => '10', + 'attributes' => ['name' => 'Acme Corp'], + ], + ], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->included())->toBeArray(); + expect($response->included())->toHaveCount(1); + expect($response->included()[0]['type'])->toBe('customers'); + expect($response->included()[0]['id'])->toBe('10'); + expect($response->included()[0]['attributes']['name'])->toBe('Acme Corp'); +}); + +it('returns empty array when no included resources exist', function () { + $mockClient = new MockClient([ + MockResponse::make([ + 'data' => [], + ]), + ]); + + $response = $this->timatic + ->withMockClient($mockClient) + ->send(new GetBudgets); + + expect($response->included())->toBeArray(); + expect($response->included())->toHaveCount(0); +}); From 4e976c25148a462e32ceafac53cf7ea885b7b8cc Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 08:27:42 +0100 Subject: [PATCH 08/62] fix phpstan --- CLAUDE.md | 4 +++- composer.json | 9 ++++++--- phpstan.neon | 9 +++++++++ 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 phpstan.neon diff --git a/CLAUDE.md b/CLAUDE.md index 49ab2a2..3b85cbf 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,5 @@ +Make sure the package is compatible with Laravel 10, 11 and 12 + === foundation rules === @@ -330,4 +332,4 @@ php generator/generate.php git checkout src/TimaticConnector.php composer dump-autoload ./vendor/bin/pest -``` \ No newline at end of file +``` diff --git a/composer.json b/composer.json index 28c35ac..e809d47 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "php": "^8.1", "saloonphp/saloon": "^3.0", "nesbot/carbon": "^2.0|^3.0", - "illuminate/support": "^10.0", + "laravel/framework": "^10.0|^11.0|^12.0", "saloonphp/pagination-plugin": "^2.0" }, "require-dev": { @@ -26,10 +26,13 @@ "phpunit/phpunit": "^10.0|^11.0", "pestphp/pest": "^2.0|^3.0", "laravel/pint": "^1.0", - "larastan/larastan": "^2.0" + "larastan/larastan": "^3.0", + "orchestra/testbench": "^10.0|^11.0|^12.0" }, "scripts": { - "test": "pest" + "test": "pest", + "format": "pint", + "analyse": "phpstan analyse --memory-limit=2G" }, "extra": { "laravel": { diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..fe0605d --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +includes: + - '%currentWorkingDirectory%/vendor/larastan/larastan/extension.neon' + +parameters: + level: 5 + paths: + - src + excludePaths: + - vendor From 68392bfdc9bea5a43433bf9118a54d4d794468b7 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 09:13:38 +0100 Subject: [PATCH 09/62] fix phpstan errors --- generator/JsonApiResourceGenerator.php | 60 +++ generator/generate.php | 77 +++- src/Dto/Attributes.php | 15 - .../GetBudgetTimeSpentTotals.php | 45 +-- src/Requests/BudgetType/GetBudgetTypes.php | 21 +- src/Requests/Change/GetChanges.php | 21 +- src/Requests/Customer/GetCustomers.php | 45 +-- .../DailyProgress/GetDailyProgresses.php | 21 +- src/Requests/Entry/GetEntries.php | 375 +++++++----------- .../EntrySuggestion/GetEntrySuggestions.php | 81 ++-- .../ExportMail/GetBudgetsExportMails.php | 21 +- src/Requests/Incident/GetIncidents.php | 21 +- src/Requests/Me/GetMes.php | 21 +- src/Requests/Overtime/GetOvertimes.php | 165 +++----- src/Requests/Team/GetTeams.php | 21 +- .../TimeSpentTotal/GetTimeSpentTotals.php | 71 ++-- src/Requests/User/GetUsers.php | 45 +-- .../GetUserCustomerHoursAggregates.php | 195 ++++----- src/Resource/Approve.php | 13 +- src/Resource/Budget.php | 128 ++---- src/Resource/Correction.php | 32 +- src/Resource/Customer.php | 79 ++-- src/Resource/Entry.php | 272 +++++-------- src/Resource/Event.php | 8 +- src/Resource/MarkAsExported.php | 13 +- src/Resource/MarkAsInvoiced.php | 13 +- src/Resource/Team.php | 75 ++-- src/Resource/User.php | 79 ++-- 28 files changed, 843 insertions(+), 1190 deletions(-) create mode 100644 generator/JsonApiResourceGenerator.php delete mode 100644 src/Dto/Attributes.php diff --git a/generator/JsonApiResourceGenerator.php b/generator/JsonApiResourceGenerator.php new file mode 100644 index 0000000..b3ae414 --- /dev/null +++ b/generator/JsonApiResourceGenerator.php @@ -0,0 +1,60 @@ +isMutationRequest($endpoint) && empty($endpoint->bodyParameters)) { + echo "Adding data parameter to {$endpoint->name}\n"; + $this->addDataParameterToMethod($endpoint, $method); + } + } + + protected function addDataParameterToMethod(Endpoint $endpoint, Method $method): void + { + // Add data parameter to method signature + $method->addParameter('data') + ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') + ->setDefaultValue(null); + + // Update method body to pass data parameter to request constructor + $body = $method->getBody(); + + // Find the "new RequestName(" pattern and add $data parameter + if (! empty($endpoint->pathParameters)) { + // Has path params: new Request($param, null) -> new Request($param, $data) + $body = preg_replace( + '/(new\s+'.preg_quote($endpoint->name, '/').'\([^)]+)\)/', + '$1, $data)', + $body + ); + } else { + // No path params: new Request() -> new Request($data) + $body = str_replace( + 'new '.$endpoint->name.'()', + 'new '.$endpoint->name.'($data)', + $body + ); + } + + $method->setBody($body); + } + + protected function isMutationRequest(Endpoint $endpoint): bool + { + return $endpoint->method->isPost() + || $endpoint->method->isPatch() + || $endpoint->method->isPut(); + } +} diff --git a/generator/generate.php b/generator/generate.php index 49f15a6..3511805 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -2,14 +2,17 @@ declare(strict_types=1); -require_once __DIR__ . '/../vendor/autoload.php'; +// Use global composer autoload for SDK generator +require_once $_SERVER['HOME'].'/.composer/vendor/autoload.php'; +// Also load local vendor for our custom generators +require_once __DIR__.'/../vendor/autoload.php'; use Crescat\SaloonSdkGenerator\CodeGenerator; use Crescat\SaloonSdkGenerator\Data\Generator\Config; use Crescat\SaloonSdkGenerator\Factory; use Timatic\SDK\Generator\JsonApiDtoGenerator; use Timatic\SDK\Generator\JsonApiRequestGenerator; -use Timatic\SDK\Generator\JsonApiPestTestGenerator; +use Timatic\SDK\Generator\JsonApiResourceGenerator; // Download OpenAPI spec echo "šŸ“„ Downloading OpenAPI specification...\n"; @@ -20,17 +23,17 @@ ], ])); -if (!$openApiJson) { +if (! $openApiJson) { echo "āŒ Failed to download OpenAPI specification\n"; exit(1); } -file_put_contents(__DIR__ . '/../openapi.json', $openApiJson); +file_put_contents(__DIR__.'/../openapi.json', $openApiJson); echo "āœ… OpenAPI specification downloaded\n\n"; // Parse the specification echo "šŸ”Ø Parsing OpenAPI specification...\n"; -$specification = Factory::parse('openapi', __DIR__ . '/../openapi.json'); +$specification = Factory::parse('openapi', __DIR__.'/../openapi.json'); echo "āœ… Specification parsed\n\n"; // Create config @@ -47,7 +50,8 @@ $generator = new CodeGenerator( config: $config, dtoGenerator: new JsonApiDtoGenerator($config), - requestGenerator: new JsonApiRequestGenerator($config) + requestGenerator: new JsonApiRequestGenerator($config), + resourceGenerator: new JsonApiResourceGenerator($config) ); // Generate the code @@ -58,17 +62,18 @@ $tests = null; // Output directory -$outputDir = __DIR__ . '/../src'; +$outputDir = __DIR__.'/../src'; // Helper function to write files -function writeFile($file, $outputDir, $namespace) { +function writeFile($file, $outputDir, $namespace) +{ $relativePath = str_replace($namespace, '', array_values($file->getNamespaces())[0]->getName()); $className = array_values($file->getClasses())[0]->getName(); - $filePath = $outputDir . str_replace('\\', '/', $relativePath) . '/' . $className . '.php'; + $filePath = $outputDir.str_replace('\\', '/', $relativePath).'/'.$className.'.php'; // Create directory if it doesn't exist $dir = dirname($filePath); - if (!is_dir($dir)) { + if (! is_dir($dir)) { mkdir($dir, 0755, true); } @@ -83,25 +88,63 @@ function writeFile($file, $outputDir, $namespace) { echo "āœ“ Connector: {$path}\n"; } -// Write resources +// Post-process and write resources echo "\nšŸ“¦ Resources:\n"; foreach ($result->resourceClasses as $resourceClass) { + // Fix POST/PUT/PATCH methods to add $data parameter + $namespace = array_values($resourceClass->getNamespaces())[0]; + $classType = array_values($namespace->getClasses())[0]; + + foreach ($classType->getMethods() as $method) { + $methodName = $method->getName(); + + // Check if it's a mutation method (post/put/patch) + if (preg_match('/^(post|put|patch)/i', $methodName)) { + // Add data parameter + $method->addParameter('data') + ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') + ->setDefaultValue(null); + + // Update method body to pass $data to request constructor + $body = $method->getBody(); + + // Pattern 2 first: new Request($param) -> new Request($param, $data) + // Must be checked before Pattern 1 to avoid double replacement + if (preg_match('/\(new\s+\w+\([^)]+\)\)/', $body)) { + $body = preg_replace( + '/\(new\s+(\w+)\(([^)]+)\)\)/', + '(new $1($2, $data))', + $body + ); + } else { + // Pattern 1: new Request() -> new Request($data) + $body = preg_replace( + '/\(new\s+(\w+)\(\)\)/', + '(new $1($data))', + $body + ); + } + + $method->setBody($body); + } + } + $path = writeFile($resourceClass, $outputDir, $config->namespace); - echo " āœ“ " . basename($path) . "\n"; + echo ' āœ“ '.basename($path)."\n"; } // Write requests echo "\nšŸ“ Requests:\n"; foreach ($result->requestClasses as $requestClass) { $path = writeFile($requestClass, $outputDir, $config->namespace); - echo " āœ“ " . basename(dirname($path)) . '/' . basename($path) . "\n"; + echo ' āœ“ '.basename(dirname($path)).'/'.basename($path)."\n"; } // Write DTOs (now Models!) echo "\nšŸŽÆ Models:\n"; foreach ($result->dtoClasses as $dtoClass) { $path = writeFile($dtoClass, $outputDir, $config->namespace); - echo " āœ“ " . basename($path) . "\n"; + echo ' āœ“ '.basename($path)."\n"; } // Write test files @@ -109,16 +152,16 @@ function writeFile($file, $outputDir, $namespace) { echo "\n🧪 Tests:\n"; foreach ($tests as $file) { if ($file instanceof \Crescat\SaloonSdkGenerator\Data\TaggedOutputFile) { - $testPath = __DIR__ . '/../' . $file->path; + $testPath = __DIR__.'/../'.$file->path; // Create directory if it doesn't exist $dir = dirname($testPath); - if (!is_dir($dir)) { + if (! is_dir($dir)) { mkdir($dir, 0755, true); } file_put_contents($testPath, $file->file); - echo " āœ“ " . basename($testPath) . "\n"; + echo ' āœ“ '.basename($testPath)."\n"; } } } diff --git a/src/Dto/Attributes.php b/src/Dto/Attributes.php deleted file mode 100644 index 8cc62c4..0000000 --- a/src/Dto/Attributes.php +++ /dev/null @@ -1,15 +0,0 @@ - $this->filterbudgetId, 'filter[budgetId][eq]' => $this->filterbudgetIdeq]); - } + protected Method $method = Method::GET; + + public function resolveEndpoint(): string + { + return '/budget-time-spent-totals'; + } + + public function __construct( + protected ?string $filterbudgetId = null, + protected ?string $filterbudgetIdeq = null, + ) {} + + public function defaultQuery(): array + { + return array_filter(['filter[budgetId]' => $this->filterbudgetId, 'filter[budgetId][eq]' => $this->filterbudgetIdeq]); + } } diff --git a/src/Requests/BudgetType/GetBudgetTypes.php b/src/Requests/BudgetType/GetBudgetTypes.php index 83903bb..693c509 100644 --- a/src/Requests/BudgetType/GetBudgetTypes.php +++ b/src/Requests/BudgetType/GetBudgetTypes.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\BudgetType; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getBudgetTypes */ -class GetBudgetTypes extends Request +class GetBudgetTypes extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/budget-types'; + } - public function resolveEndpoint(): string - { - return "/budget-types"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/Change/GetChanges.php b/src/Requests/Change/GetChanges.php index 892709c..9239f24 100644 --- a/src/Requests/Change/GetChanges.php +++ b/src/Requests/Change/GetChanges.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\Change; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getChanges */ -class GetChanges extends Request +class GetChanges extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/changes'; + } - public function resolveEndpoint(): string - { - return "/changes"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/Customer/GetCustomers.php b/src/Requests/Customer/GetCustomers.php index e544091..4b72f3f 100644 --- a/src/Requests/Customer/GetCustomers.php +++ b/src/Requests/Customer/GetCustomers.php @@ -2,38 +2,29 @@ namespace Timatic\SDK\Requests\Customer; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getCustomers */ -class GetCustomers extends Request +class GetCustomers extends Request implements Paginatable { - protected Method $method = Method::GET; - - - public function resolveEndpoint(): string - { - return "/customers"; - } - - - /** - * @param null|string $filterexternalId - * @param null|string $filterexternalIdeq - */ - public function __construct( - protected ?string $filterexternalId = null, - protected ?string $filterexternalIdeq = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter(['filter[externalId]' => $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); - } + protected Method $method = Method::GET; + + public function resolveEndpoint(): string + { + return '/customers'; + } + + public function __construct( + protected ?string $filterexternalId = null, + protected ?string $filterexternalIdeq = null, + ) {} + + public function defaultQuery(): array + { + return array_filter(['filter[externalId]' => $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); + } } diff --git a/src/Requests/DailyProgress/GetDailyProgresses.php b/src/Requests/DailyProgress/GetDailyProgresses.php index 0578ec1..0e8f5b2 100644 --- a/src/Requests/DailyProgress/GetDailyProgresses.php +++ b/src/Requests/DailyProgress/GetDailyProgresses.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\DailyProgress; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getDailyProgresses */ -class GetDailyProgresses extends Request +class GetDailyProgresses extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/daily-progress'; + } - public function resolveEndpoint(): string - { - return "/daily-progress"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/Entry/GetEntries.php b/src/Requests/Entry/GetEntries.php index 708d475..a2be396 100644 --- a/src/Requests/Entry/GetEntries.php +++ b/src/Requests/Entry/GetEntries.php @@ -2,239 +2,164 @@ namespace Timatic\SDK\Requests\Entry; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getEntries */ -class GetEntries extends Request +class GetEntries extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/entries'; + } - public function resolveEndpoint(): string - { - return "/entries"; - } + public function __construct( + protected ?string $filteruserId = null, + protected ?string $filteruserIdeq = null, + protected ?string $filteruserIdnq = null, + protected ?string $filteruserIdgt = null, + protected ?string $filteruserIdlt = null, + protected ?string $filteruserIdgte = null, + protected ?string $filteruserIdlte = null, + protected ?string $filteruserIdcontains = null, + protected ?string $filterbudgetId = null, + protected ?string $filterbudgetIdeq = null, + protected ?string $filterbudgetIdnq = null, + protected ?string $filterbudgetIdgt = null, + protected ?string $filterbudgetIdlt = null, + protected ?string $filterbudgetIdgte = null, + protected ?string $filterbudgetIdlte = null, + protected ?string $filterbudgetIdcontains = null, + protected ?string $filterstartedAt = null, + protected ?string $filterstartedAteq = null, + protected ?string $filterstartedAtnq = null, + protected ?string $filterstartedAtgt = null, + protected ?string $filterstartedAtlt = null, + protected ?string $filterstartedAtgte = null, + protected ?string $filterstartedAtlte = null, + protected ?string $filterstartedAtcontains = null, + protected ?string $filterendedAt = null, + protected ?string $filterendedAteq = null, + protected ?string $filterendedAtnq = null, + protected ?string $filterendedAtgt = null, + protected ?string $filterendedAtlt = null, + protected ?string $filterendedAtgte = null, + protected ?string $filterendedAtlte = null, + protected ?string $filterendedAtcontains = null, + protected ?string $filterhasOvertime = null, + protected ?string $filterhasOvertimeeq = null, + protected ?string $filterhasOvertimenq = null, + protected ?string $filterhasOvertimegt = null, + protected ?string $filterhasOvertimelt = null, + protected ?string $filterhasOvertimegte = null, + protected ?string $filterhasOvertimelte = null, + protected ?string $filterhasOvertimecontains = null, + protected ?string $filteruserFullName = null, + protected ?string $filteruserFullNameeq = null, + protected ?string $filteruserFullNamenq = null, + protected ?string $filteruserFullNamegt = null, + protected ?string $filteruserFullNamelt = null, + protected ?string $filteruserFullNamegte = null, + protected ?string $filteruserFullNamelte = null, + protected ?string $filteruserFullNamecontains = null, + protected ?string $filtercustomerId = null, + protected ?string $filtercustomerIdeq = null, + protected ?string $filtercustomerIdnq = null, + protected ?string $filtercustomerIdgt = null, + protected ?string $filtercustomerIdlt = null, + protected ?string $filtercustomerIdgte = null, + protected ?string $filtercustomerIdlte = null, + protected ?string $filtercustomerIdcontains = null, + protected ?string $filterticketNumber = null, + protected ?string $filterticketNumbereq = null, + protected ?string $filterticketNumbernq = null, + protected ?string $filterticketNumbergt = null, + protected ?string $filterticketNumberlt = null, + protected ?string $filterticketNumbergte = null, + protected ?string $filterticketNumberlte = null, + protected ?string $filterticketNumbercontains = null, + protected ?string $filtersettlement = null, + protected ?string $filterisInvoiced = null, + protected ?string $filterisInvoiceable = null, + protected ?string $include = null, + ) {} - - /** - * @param null|string $filteruserId - * @param null|string $filteruserIdeq - * @param null|string $filteruserIdnq - * @param null|string $filteruserIdgt - * @param null|string $filteruserIdlt - * @param null|string $filteruserIdgte - * @param null|string $filteruserIdlte - * @param null|string $filteruserIdcontains - * @param null|string $filterbudgetId - * @param null|string $filterbudgetIdeq - * @param null|string $filterbudgetIdnq - * @param null|string $filterbudgetIdgt - * @param null|string $filterbudgetIdlt - * @param null|string $filterbudgetIdgte - * @param null|string $filterbudgetIdlte - * @param null|string $filterbudgetIdcontains - * @param null|string $filterstartedAt - * @param null|string $filterstartedAteq - * @param null|string $filterstartedAtnq - * @param null|string $filterstartedAtgt - * @param null|string $filterstartedAtlt - * @param null|string $filterstartedAtgte - * @param null|string $filterstartedAtlte - * @param null|string $filterstartedAtcontains - * @param null|string $filterendedAt - * @param null|string $filterendedAteq - * @param null|string $filterendedAtnq - * @param null|string $filterendedAtgt - * @param null|string $filterendedAtlt - * @param null|string $filterendedAtgte - * @param null|string $filterendedAtlte - * @param null|string $filterendedAtcontains - * @param null|string $filterhasOvertime - * @param null|string $filterhasOvertimeeq - * @param null|string $filterhasOvertimenq - * @param null|string $filterhasOvertimegt - * @param null|string $filterhasOvertimelt - * @param null|string $filterhasOvertimegte - * @param null|string $filterhasOvertimelte - * @param null|string $filterhasOvertimecontains - * @param null|string $filteruserFullName - * @param null|string $filteruserFullNameeq - * @param null|string $filteruserFullNamenq - * @param null|string $filteruserFullNamegt - * @param null|string $filteruserFullNamelt - * @param null|string $filteruserFullNamegte - * @param null|string $filteruserFullNamelte - * @param null|string $filteruserFullNamecontains - * @param null|string $filtercustomerId - * @param null|string $filtercustomerIdeq - * @param null|string $filtercustomerIdnq - * @param null|string $filtercustomerIdgt - * @param null|string $filtercustomerIdlt - * @param null|string $filtercustomerIdgte - * @param null|string $filtercustomerIdlte - * @param null|string $filtercustomerIdcontains - * @param null|string $filterticketNumber - * @param null|string $filterticketNumbereq - * @param null|string $filterticketNumbernq - * @param null|string $filterticketNumbergt - * @param null|string $filterticketNumberlt - * @param null|string $filterticketNumbergte - * @param null|string $filterticketNumberlte - * @param null|string $filterticketNumbercontains - * @param null|string $filtersettlement - * @param null|string $filterisInvoiced - * @param null|string $filterisInvoiceable - * @param null|string $include - */ - public function __construct( - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - protected ?string $filteruserIdnq = null, - protected ?string $filteruserIdgt = null, - protected ?string $filteruserIdlt = null, - protected ?string $filteruserIdgte = null, - protected ?string $filteruserIdlte = null, - protected ?string $filteruserIdcontains = null, - protected ?string $filterbudgetId = null, - protected ?string $filterbudgetIdeq = null, - protected ?string $filterbudgetIdnq = null, - protected ?string $filterbudgetIdgt = null, - protected ?string $filterbudgetIdlt = null, - protected ?string $filterbudgetIdgte = null, - protected ?string $filterbudgetIdlte = null, - protected ?string $filterbudgetIdcontains = null, - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterhasOvertime = null, - protected ?string $filterhasOvertimeeq = null, - protected ?string $filterhasOvertimenq = null, - protected ?string $filterhasOvertimegt = null, - protected ?string $filterhasOvertimelt = null, - protected ?string $filterhasOvertimegte = null, - protected ?string $filterhasOvertimelte = null, - protected ?string $filterhasOvertimecontains = null, - protected ?string $filteruserFullName = null, - protected ?string $filteruserFullNameeq = null, - protected ?string $filteruserFullNamenq = null, - protected ?string $filteruserFullNamegt = null, - protected ?string $filteruserFullNamelt = null, - protected ?string $filteruserFullNamegte = null, - protected ?string $filteruserFullNamelte = null, - protected ?string $filteruserFullNamecontains = null, - protected ?string $filtercustomerId = null, - protected ?string $filtercustomerIdeq = null, - protected ?string $filtercustomerIdnq = null, - protected ?string $filtercustomerIdgt = null, - protected ?string $filtercustomerIdlt = null, - protected ?string $filtercustomerIdgte = null, - protected ?string $filtercustomerIdlte = null, - protected ?string $filtercustomerIdcontains = null, - protected ?string $filterticketNumber = null, - protected ?string $filterticketNumbereq = null, - protected ?string $filterticketNumbernq = null, - protected ?string $filterticketNumbergt = null, - protected ?string $filterticketNumberlt = null, - protected ?string $filterticketNumbergte = null, - protected ?string $filterticketNumberlte = null, - protected ?string $filterticketNumbercontains = null, - protected ?string $filtersettlement = null, - protected ?string $filterisInvoiced = null, - protected ?string $filterisInvoiceable = null, - protected ?string $include = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - 'filter[userId][nq]' => $this->filteruserIdnq, - 'filter[userId][gt]' => $this->filteruserIdgt, - 'filter[userId][lt]' => $this->filteruserIdlt, - 'filter[userId][gte]' => $this->filteruserIdgte, - 'filter[userId][lte]' => $this->filteruserIdlte, - 'filter[userId][contains]' => $this->filteruserIdcontains, - 'filter[budgetId]' => $this->filterbudgetId, - 'filter[budgetId][eq]' => $this->filterbudgetIdeq, - 'filter[budgetId][nq]' => $this->filterbudgetIdnq, - 'filter[budgetId][gt]' => $this->filterbudgetIdgt, - 'filter[budgetId][lt]' => $this->filterbudgetIdlt, - 'filter[budgetId][gte]' => $this->filterbudgetIdgte, - 'filter[budgetId][lte]' => $this->filterbudgetIdlte, - 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[hasOvertime]' => $this->filterhasOvertime, - 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, - 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, - 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, - 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, - 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, - 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, - 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, - 'filter[userFullName]' => $this->filteruserFullName, - 'filter[userFullName][eq]' => $this->filteruserFullNameeq, - 'filter[userFullName][nq]' => $this->filteruserFullNamenq, - 'filter[userFullName][gt]' => $this->filteruserFullNamegt, - 'filter[userFullName][lt]' => $this->filteruserFullNamelt, - 'filter[userFullName][gte]' => $this->filteruserFullNamegte, - 'filter[userFullName][lte]' => $this->filteruserFullNamelte, - 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, - 'filter[customerId]' => $this->filtercustomerId, - 'filter[customerId][eq]' => $this->filtercustomerIdeq, - 'filter[customerId][nq]' => $this->filtercustomerIdnq, - 'filter[customerId][gt]' => $this->filtercustomerIdgt, - 'filter[customerId][lt]' => $this->filtercustomerIdlt, - 'filter[customerId][gte]' => $this->filtercustomerIdgte, - 'filter[customerId][lte]' => $this->filtercustomerIdlte, - 'filter[customerId][contains]' => $this->filtercustomerIdcontains, - 'filter[ticketNumber]' => $this->filterticketNumber, - 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, - 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, - 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, - 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, - 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, - 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, - 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, - 'filter[settlement]' => $this->filtersettlement, - 'filter[isInvoiced]' => $this->filterisInvoiced, - 'filter[isInvoiceable]' => $this->filterisInvoiceable, - 'include' => $this->include, - ]); - } + public function defaultQuery(): array + { + return array_filter([ + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + 'filter[userId][nq]' => $this->filteruserIdnq, + 'filter[userId][gt]' => $this->filteruserIdgt, + 'filter[userId][lt]' => $this->filteruserIdlt, + 'filter[userId][gte]' => $this->filteruserIdgte, + 'filter[userId][lte]' => $this->filteruserIdlte, + 'filter[userId][contains]' => $this->filteruserIdcontains, + 'filter[budgetId]' => $this->filterbudgetId, + 'filter[budgetId][eq]' => $this->filterbudgetIdeq, + 'filter[budgetId][nq]' => $this->filterbudgetIdnq, + 'filter[budgetId][gt]' => $this->filterbudgetIdgt, + 'filter[budgetId][lt]' => $this->filterbudgetIdlt, + 'filter[budgetId][gte]' => $this->filterbudgetIdgte, + 'filter[budgetId][lte]' => $this->filterbudgetIdlte, + 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, + 'filter[startedAt]' => $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[hasOvertime]' => $this->filterhasOvertime, + 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, + 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, + 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, + 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, + 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, + 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, + 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, + 'filter[userFullName]' => $this->filteruserFullName, + 'filter[userFullName][eq]' => $this->filteruserFullNameeq, + 'filter[userFullName][nq]' => $this->filteruserFullNamenq, + 'filter[userFullName][gt]' => $this->filteruserFullNamegt, + 'filter[userFullName][lt]' => $this->filteruserFullNamelt, + 'filter[userFullName][gte]' => $this->filteruserFullNamegte, + 'filter[userFullName][lte]' => $this->filteruserFullNamelte, + 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, + 'filter[customerId]' => $this->filtercustomerId, + 'filter[customerId][eq]' => $this->filtercustomerIdeq, + 'filter[customerId][nq]' => $this->filtercustomerIdnq, + 'filter[customerId][gt]' => $this->filtercustomerIdgt, + 'filter[customerId][lt]' => $this->filtercustomerIdlt, + 'filter[customerId][gte]' => $this->filtercustomerIdgte, + 'filter[customerId][lte]' => $this->filtercustomerIdlte, + 'filter[customerId][contains]' => $this->filtercustomerIdcontains, + 'filter[ticketNumber]' => $this->filterticketNumber, + 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, + 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, + 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, + 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, + 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, + 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, + 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, + 'filter[settlement]' => $this->filtersettlement, + 'filter[isInvoiced]' => $this->filterisInvoiced, + 'filter[isInvoiceable]' => $this->filterisInvoiceable, + 'include' => $this->include, + ]); + } } diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestions.php b/src/Requests/EntrySuggestion/GetEntrySuggestions.php index b6d28fe..e283752 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestions.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestions.php @@ -2,59 +2,44 @@ namespace Timatic\SDK\Requests\EntrySuggestion; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getEntrySuggestions */ -class GetEntrySuggestions extends Request +class GetEntrySuggestions extends Request implements Paginatable { - protected Method $method = Method::GET; - - - public function resolveEndpoint(): string - { - return "/entry-suggestions"; - } - - - /** - * @param null|string $filterdate - * @param null|string $filterdateeq - * @param null|string $filterdatenq - * @param null|string $filterdategt - * @param null|string $filterdatelt - * @param null|string $filterdategte - * @param null|string $filterdatelte - * @param null|string $filterdatecontains - */ - public function __construct( - protected ?string $filterdate = null, - protected ?string $filterdateeq = null, - protected ?string $filterdatenq = null, - protected ?string $filterdategt = null, - protected ?string $filterdatelt = null, - protected ?string $filterdategte = null, - protected ?string $filterdatelte = null, - protected ?string $filterdatecontains = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[date]' => $this->filterdate, - 'filter[date][eq]' => $this->filterdateeq, - 'filter[date][nq]' => $this->filterdatenq, - 'filter[date][gt]' => $this->filterdategt, - 'filter[date][lt]' => $this->filterdatelt, - 'filter[date][gte]' => $this->filterdategte, - 'filter[date][lte]' => $this->filterdatelte, - 'filter[date][contains]' => $this->filterdatecontains, - ]); - } + protected Method $method = Method::GET; + + public function resolveEndpoint(): string + { + return '/entry-suggestions'; + } + + public function __construct( + protected ?string $filterdate = null, + protected ?string $filterdateeq = null, + protected ?string $filterdatenq = null, + protected ?string $filterdategt = null, + protected ?string $filterdatelt = null, + protected ?string $filterdategte = null, + protected ?string $filterdatelte = null, + protected ?string $filterdatecontains = null, + ) {} + + public function defaultQuery(): array + { + return array_filter([ + 'filter[date]' => $this->filterdate, + 'filter[date][eq]' => $this->filterdateeq, + 'filter[date][nq]' => $this->filterdatenq, + 'filter[date][gt]' => $this->filterdategt, + 'filter[date][lt]' => $this->filterdatelt, + 'filter[date][gte]' => $this->filterdategte, + 'filter[date][lte]' => $this->filterdatelte, + 'filter[date][contains]' => $this->filterdatecontains, + ]); + } } diff --git a/src/Requests/ExportMail/GetBudgetsExportMails.php b/src/Requests/ExportMail/GetBudgetsExportMails.php index d8e896e..2b302c3 100644 --- a/src/Requests/ExportMail/GetBudgetsExportMails.php +++ b/src/Requests/ExportMail/GetBudgetsExportMails.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\ExportMail; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getBudgetsExportMails */ -class GetBudgetsExportMails extends Request +class GetBudgetsExportMails extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/budgets/export-mail'; + } - public function resolveEndpoint(): string - { - return "/budgets/export-mail"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/Incident/GetIncidents.php b/src/Requests/Incident/GetIncidents.php index ea1d698..bdf6792 100644 --- a/src/Requests/Incident/GetIncidents.php +++ b/src/Requests/Incident/GetIncidents.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\Incident; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getIncidents */ -class GetIncidents extends Request +class GetIncidents extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/incidents'; + } - public function resolveEndpoint(): string - { - return "/incidents"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/Me/GetMes.php b/src/Requests/Me/GetMes.php index d46f28f..c9858ac 100644 --- a/src/Requests/Me/GetMes.php +++ b/src/Requests/Me/GetMes.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\Me; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getMes */ -class GetMes extends Request +class GetMes extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/me'; + } - public function resolveEndpoint(): string - { - return "/me"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/Overtime/GetOvertimes.php b/src/Requests/Overtime/GetOvertimes.php index a8dd8f7..e06e44e 100644 --- a/src/Requests/Overtime/GetOvertimes.php +++ b/src/Requests/Overtime/GetOvertimes.php @@ -2,113 +2,80 @@ namespace Timatic\SDK\Requests\Overtime; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getOvertimes */ -class GetOvertimes extends Request +class GetOvertimes extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/overtimes'; + } - public function resolveEndpoint(): string - { - return "/overtimes"; - } + public function __construct( + protected ?string $filterstartedAt = null, + protected ?string $filterstartedAteq = null, + protected ?string $filterstartedAtnq = null, + protected ?string $filterstartedAtgt = null, + protected ?string $filterstartedAtlt = null, + protected ?string $filterstartedAtgte = null, + protected ?string $filterstartedAtlte = null, + protected ?string $filterstartedAtcontains = null, + protected ?string $filterendedAt = null, + protected ?string $filterendedAteq = null, + protected ?string $filterendedAtnq = null, + protected ?string $filterendedAtgt = null, + protected ?string $filterendedAtlt = null, + protected ?string $filterendedAtgte = null, + protected ?string $filterendedAtlte = null, + protected ?string $filterendedAtcontains = null, + protected ?string $filterisApproved = null, + protected ?string $filterapprovedAt = null, + protected ?string $filterapprovedAteq = null, + protected ?string $filterapprovedAtnq = null, + protected ?string $filterapprovedAtgt = null, + protected ?string $filterapprovedAtlt = null, + protected ?string $filterapprovedAtgte = null, + protected ?string $filterapprovedAtlte = null, + protected ?string $filterapprovedAtcontains = null, + protected ?string $filterisExported = null, + ) {} - - /** - * @param null|string $filterstartedAt - * @param null|string $filterstartedAteq - * @param null|string $filterstartedAtnq - * @param null|string $filterstartedAtgt - * @param null|string $filterstartedAtlt - * @param null|string $filterstartedAtgte - * @param null|string $filterstartedAtlte - * @param null|string $filterstartedAtcontains - * @param null|string $filterendedAt - * @param null|string $filterendedAteq - * @param null|string $filterendedAtnq - * @param null|string $filterendedAtgt - * @param null|string $filterendedAtlt - * @param null|string $filterendedAtgte - * @param null|string $filterendedAtlte - * @param null|string $filterendedAtcontains - * @param null|string $filterisApproved - * @param null|string $filterapprovedAt - * @param null|string $filterapprovedAteq - * @param null|string $filterapprovedAtnq - * @param null|string $filterapprovedAtgt - * @param null|string $filterapprovedAtlt - * @param null|string $filterapprovedAtgte - * @param null|string $filterapprovedAtlte - * @param null|string $filterapprovedAtcontains - * @param null|string $filterisExported - */ - public function __construct( - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterisApproved = null, - protected ?string $filterapprovedAt = null, - protected ?string $filterapprovedAteq = null, - protected ?string $filterapprovedAtnq = null, - protected ?string $filterapprovedAtgt = null, - protected ?string $filterapprovedAtlt = null, - protected ?string $filterapprovedAtgte = null, - protected ?string $filterapprovedAtlte = null, - protected ?string $filterapprovedAtcontains = null, - protected ?string $filterisExported = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[isApproved]' => $this->filterisApproved, - 'filter[approvedAt]' => $this->filterapprovedAt, - 'filter[approvedAt][eq]' => $this->filterapprovedAteq, - 'filter[approvedAt][nq]' => $this->filterapprovedAtnq, - 'filter[approvedAt][gt]' => $this->filterapprovedAtgt, - 'filter[approvedAt][lt]' => $this->filterapprovedAtlt, - 'filter[approvedAt][gte]' => $this->filterapprovedAtgte, - 'filter[approvedAt][lte]' => $this->filterapprovedAtlte, - 'filter[approvedAt][contains]' => $this->filterapprovedAtcontains, - 'filter[isExported]' => $this->filterisExported, - ]); - } + public function defaultQuery(): array + { + return array_filter([ + 'filter[startedAt]' => $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[isApproved]' => $this->filterisApproved, + 'filter[approvedAt]' => $this->filterapprovedAt, + 'filter[approvedAt][eq]' => $this->filterapprovedAteq, + 'filter[approvedAt][nq]' => $this->filterapprovedAtnq, + 'filter[approvedAt][gt]' => $this->filterapprovedAtgt, + 'filter[approvedAt][lt]' => $this->filterapprovedAtlt, + 'filter[approvedAt][gte]' => $this->filterapprovedAtgte, + 'filter[approvedAt][lte]' => $this->filterapprovedAtlte, + 'filter[approvedAt][contains]' => $this->filterapprovedAtcontains, + 'filter[isExported]' => $this->filterisExported, + ]); + } } diff --git a/src/Requests/Team/GetTeams.php b/src/Requests/Team/GetTeams.php index 555a8d9..172cb2d 100644 --- a/src/Requests/Team/GetTeams.php +++ b/src/Requests/Team/GetTeams.php @@ -2,26 +2,21 @@ namespace Timatic\SDK\Requests\Team; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getTeams */ -class GetTeams extends Request +class GetTeams extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/teams'; + } - public function resolveEndpoint(): string - { - return "/teams"; - } - - - public function __construct() - { - } + public function __construct() {} } diff --git a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php b/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php index ee326a1..96d32f8 100644 --- a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php +++ b/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php @@ -2,53 +2,40 @@ namespace Timatic\SDK\Requests\TimeSpentTotal; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getTimeSpentTotals */ -class GetTimeSpentTotals extends Request +class GetTimeSpentTotals extends Request implements Paginatable { - protected Method $method = Method::GET; - - - public function resolveEndpoint(): string - { - return "/time-spent-totals"; - } - - - /** - * @param null|string $filterstartedAtgte - * @param null|string $filterstartedAtlte - * @param null|string $filterteamId - * @param null|string $filterteamIdeq - * @param null|string $filteruserId - * @param null|string $filteruserIdeq - */ - public function __construct( - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterteamId = null, - protected ?string $filterteamIdeq = null, - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[teamId]' => $this->filterteamId, - 'filter[teamId][eq]' => $this->filterteamIdeq, - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - ]); - } + protected Method $method = Method::GET; + + public function resolveEndpoint(): string + { + return '/time-spent-totals'; + } + + public function __construct( + protected ?string $filterstartedAtgte = null, + protected ?string $filterstartedAtlte = null, + protected ?string $filterteamId = null, + protected ?string $filterteamIdeq = null, + protected ?string $filteruserId = null, + protected ?string $filteruserIdeq = null, + ) {} + + public function defaultQuery(): array + { + return array_filter([ + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[teamId]' => $this->filterteamId, + 'filter[teamId][eq]' => $this->filterteamIdeq, + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + ]); + } } diff --git a/src/Requests/User/GetUsers.php b/src/Requests/User/GetUsers.php index 12635a6..2c81309 100644 --- a/src/Requests/User/GetUsers.php +++ b/src/Requests/User/GetUsers.php @@ -2,38 +2,29 @@ namespace Timatic\SDK\Requests\User; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getUsers */ -class GetUsers extends Request +class GetUsers extends Request implements Paginatable { - protected Method $method = Method::GET; - - - public function resolveEndpoint(): string - { - return "/users"; - } - - - /** - * @param null|string $filterexternalId - * @param null|string $filterexternalIdeq - */ - public function __construct( - protected ?string $filterexternalId = null, - protected ?string $filterexternalIdeq = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter(['filter[externalId]' => $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); - } + protected Method $method = Method::GET; + + public function resolveEndpoint(): string + { + return '/users'; + } + + public function __construct( + protected ?string $filterexternalId = null, + protected ?string $filterexternalIdeq = null, + ) {} + + public function defaultQuery(): array + { + return array_filter(['filter[externalId]' => $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); + } } diff --git a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php index 587ae21..9d190c8 100644 --- a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php +++ b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php @@ -2,131 +2,92 @@ namespace Timatic\SDK\Requests\UserCustomerHoursAggregate; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; +use Saloon\PaginationPlugin\Contracts\Paginatable; /** * getUserCustomerHoursAggregates */ -class GetUserCustomerHoursAggregates extends Request +class GetUserCustomerHoursAggregates extends Request implements Paginatable { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return '/user-customer-hours-aggregates'; + } - public function resolveEndpoint(): string - { - return "/user-customer-hours-aggregates"; - } + public function __construct( + protected ?string $filterstartedAt = null, + protected ?string $filterstartedAteq = null, + protected ?string $filterstartedAtnq = null, + protected ?string $filterstartedAtgt = null, + protected ?string $filterstartedAtlt = null, + protected ?string $filterstartedAtgte = null, + protected ?string $filterstartedAtlte = null, + protected ?string $filterstartedAtcontains = null, + protected ?string $filterendedAt = null, + protected ?string $filterendedAteq = null, + protected ?string $filterendedAtnq = null, + protected ?string $filterendedAtgt = null, + protected ?string $filterendedAtlt = null, + protected ?string $filterendedAtgte = null, + protected ?string $filterendedAtlte = null, + protected ?string $filterendedAtcontains = null, + protected ?string $filterteamId = null, + protected ?string $filterteamIdeq = null, + protected ?string $filterteamIdnq = null, + protected ?string $filterteamIdgt = null, + protected ?string $filterteamIdlt = null, + protected ?string $filterteamIdgte = null, + protected ?string $filterteamIdlte = null, + protected ?string $filterteamIdcontains = null, + protected ?string $filteruserId = null, + protected ?string $filteruserIdeq = null, + protected ?string $filteruserIdnq = null, + protected ?string $filteruserIdgt = null, + protected ?string $filteruserIdlt = null, + protected ?string $filteruserIdgte = null, + protected ?string $filteruserIdlte = null, + protected ?string $filteruserIdcontains = null, + ) {} - - /** - * @param null|string $filterstartedAt - * @param null|string $filterstartedAteq - * @param null|string $filterstartedAtnq - * @param null|string $filterstartedAtgt - * @param null|string $filterstartedAtlt - * @param null|string $filterstartedAtgte - * @param null|string $filterstartedAtlte - * @param null|string $filterstartedAtcontains - * @param null|string $filterendedAt - * @param null|string $filterendedAteq - * @param null|string $filterendedAtnq - * @param null|string $filterendedAtgt - * @param null|string $filterendedAtlt - * @param null|string $filterendedAtgte - * @param null|string $filterendedAtlte - * @param null|string $filterendedAtcontains - * @param null|string $filterteamId - * @param null|string $filterteamIdeq - * @param null|string $filterteamIdnq - * @param null|string $filterteamIdgt - * @param null|string $filterteamIdlt - * @param null|string $filterteamIdgte - * @param null|string $filterteamIdlte - * @param null|string $filterteamIdcontains - * @param null|string $filteruserId - * @param null|string $filteruserIdeq - * @param null|string $filteruserIdnq - * @param null|string $filteruserIdgt - * @param null|string $filteruserIdlt - * @param null|string $filteruserIdgte - * @param null|string $filteruserIdlte - * @param null|string $filteruserIdcontains - */ - public function __construct( - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterteamId = null, - protected ?string $filterteamIdeq = null, - protected ?string $filterteamIdnq = null, - protected ?string $filterteamIdgt = null, - protected ?string $filterteamIdlt = null, - protected ?string $filterteamIdgte = null, - protected ?string $filterteamIdlte = null, - protected ?string $filterteamIdcontains = null, - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - protected ?string $filteruserIdnq = null, - protected ?string $filteruserIdgt = null, - protected ?string $filteruserIdlt = null, - protected ?string $filteruserIdgte = null, - protected ?string $filteruserIdlte = null, - protected ?string $filteruserIdcontains = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[teamId]' => $this->filterteamId, - 'filter[teamId][eq]' => $this->filterteamIdeq, - 'filter[teamId][nq]' => $this->filterteamIdnq, - 'filter[teamId][gt]' => $this->filterteamIdgt, - 'filter[teamId][lt]' => $this->filterteamIdlt, - 'filter[teamId][gte]' => $this->filterteamIdgte, - 'filter[teamId][lte]' => $this->filterteamIdlte, - 'filter[teamId][contains]' => $this->filterteamIdcontains, - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - 'filter[userId][nq]' => $this->filteruserIdnq, - 'filter[userId][gt]' => $this->filteruserIdgt, - 'filter[userId][lt]' => $this->filteruserIdlt, - 'filter[userId][gte]' => $this->filteruserIdgte, - 'filter[userId][lte]' => $this->filteruserIdlte, - 'filter[userId][contains]' => $this->filteruserIdcontains, - ]); - } + public function defaultQuery(): array + { + return array_filter([ + 'filter[startedAt]' => $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[teamId]' => $this->filterteamId, + 'filter[teamId][eq]' => $this->filterteamIdeq, + 'filter[teamId][nq]' => $this->filterteamIdnq, + 'filter[teamId][gt]' => $this->filterteamIdgt, + 'filter[teamId][lt]' => $this->filterteamIdlt, + 'filter[teamId][gte]' => $this->filterteamIdgte, + 'filter[teamId][lte]' => $this->filterteamIdlte, + 'filter[teamId][contains]' => $this->filterteamIdcontains, + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + 'filter[userId][nq]' => $this->filteruserIdnq, + 'filter[userId][gt]' => $this->filteruserIdgt, + 'filter[userId][lt]' => $this->filteruserIdlt, + 'filter[userId][gte]' => $this->filteruserIdgte, + 'filter[userId][lte]' => $this->filteruserIdlte, + 'filter[userId][contains]' => $this->filteruserIdcontains, + ]); + } } diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php index 857b072..d233240 100644 --- a/src/Resource/Approve.php +++ b/src/Resource/Approve.php @@ -8,11 +8,10 @@ class Approve extends BaseResource { - /** - * @param string $overtime - */ - public function postOvertimeApprove(string $overtime): Response - { - return $this->connector->send(new PostOvertimeApprove($overtime)); - } + public function postOvertimeApprove( + string $overtime, + \Timatic\SDK\Foundation\Model|array|null $data = null, + ): Response { + return $this->connector->send(new PostOvertimeApprove($overtime, $data)); + } } diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index f1a2b36..d7a7b58 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -13,93 +13,53 @@ class Budget extends BaseResource { - /** - * @param int $filtercustomerId - * @param int $filtercustomerIdeq - * @param int $filtercustomerIdnq - * @param int $filtercustomerIdgt - * @param int $filtercustomerIdlt - * @param int $filtercustomerIdgte - * @param int $filtercustomerIdlte - * @param int $filtercustomerIdcontains - * @param string $filterbudgetTypeId - * @param string $filterbudgetTypeIdeq - * @param string $filterbudgetTypeIdnq - * @param string $filterbudgetTypeIdgt - * @param string $filterbudgetTypeIdlt - * @param string $filterbudgetTypeIdgte - * @param string $filterbudgetTypeIdlte - * @param string $filterbudgetTypeIdcontains - * @param string $filterisArchived - * @param string $filtercustomerExternalId - * @param string $filtershowToCustomer - * @param string $include - */ - public function getBudgets( - ?int $filtercustomerId = null, - ?int $filtercustomerIdeq = null, - ?int $filtercustomerIdnq = null, - ?int $filtercustomerIdgt = null, - ?int $filtercustomerIdlt = null, - ?int $filtercustomerIdgte = null, - ?int $filtercustomerIdlte = null, - ?int $filtercustomerIdcontains = null, - ?string $filterbudgetTypeId = null, - ?string $filterbudgetTypeIdeq = null, - ?string $filterbudgetTypeIdnq = null, - ?string $filterbudgetTypeIdgt = null, - ?string $filterbudgetTypeIdlt = null, - ?string $filterbudgetTypeIdgte = null, - ?string $filterbudgetTypeIdlte = null, - ?string $filterbudgetTypeIdcontains = null, - ?string $filterisArchived = null, - ?string $filtercustomerExternalId = null, - ?string $filtershowToCustomer = null, - ?string $include = null, - ): Response - { - return $this->connector->send(new GetBudgets($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); - } + public function getBudgets( + ?int $filtercustomerId = null, + ?int $filtercustomerIdeq = null, + ?int $filtercustomerIdnq = null, + ?int $filtercustomerIdgt = null, + ?int $filtercustomerIdlt = null, + ?int $filtercustomerIdgte = null, + ?int $filtercustomerIdlte = null, + ?int $filtercustomerIdcontains = null, + ?string $filterbudgetTypeId = null, + ?string $filterbudgetTypeIdeq = null, + ?string $filterbudgetTypeIdnq = null, + ?string $filterbudgetTypeIdgt = null, + ?string $filterbudgetTypeIdlt = null, + ?string $filterbudgetTypeIdgte = null, + ?string $filterbudgetTypeIdlte = null, + ?string $filterbudgetTypeIdcontains = null, + ?string $filterisArchived = null, + ?string $filtercustomerExternalId = null, + ?string $filtershowToCustomer = null, + ?string $include = null, + ): Response { + return $this->connector->send(new GetBudgets($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); + } + public function postBudgets(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostBudgets($data)); + } - public function postBudgets(): Response - { - return $this->connector->send(new PostBudgets()); - } + public function getBudget(string $budget): Response + { + return $this->connector->send(new GetBudget($budget)); + } + public function putBudget(string $budget, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PutBudget($budget, $data)); + } - /** - * @param string $budget - */ - public function getBudget(string $budget): Response - { - return $this->connector->send(new GetBudget($budget)); - } + public function deleteBudget(string $budget): Response + { + return $this->connector->send(new DeleteBudget($budget)); + } - - /** - * @param string $budget - */ - public function putBudget(string $budget): Response - { - return $this->connector->send(new PutBudget($budget)); - } - - - /** - * @param string $budget - */ - public function deleteBudget(string $budget): Response - { - return $this->connector->send(new DeleteBudget($budget)); - } - - - /** - * @param string $budget - */ - public function patchBudget(string $budget): Response - { - return $this->connector->send(new PatchBudget($budget)); - } + public function patchBudget(string $budget, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PatchBudget($budget, $data)); + } } diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index 6d3112f..4c93365 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -10,26 +10,18 @@ class Correction extends BaseResource { - public function postCorrections(): Response - { - return $this->connector->send(new PostCorrections()); - } + public function postCorrections(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostCorrections($data)); + } + public function putCorrection(string $correction, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PutCorrection($correction, $data)); + } - /** - * @param string $correction - */ - public function putCorrection(string $correction): Response - { - return $this->connector->send(new PutCorrection($correction)); - } - - - /** - * @param string $correction - */ - public function patchCorrection(string $correction): Response - { - return $this->connector->send(new PatchCorrection($correction)); - } + public function patchCorrection(string $correction, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PatchCorrection($correction, $data)); + } } diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index a568595..1c14e56 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -13,54 +13,33 @@ class Customer extends BaseResource { - /** - * @param string $filterexternalId - * @param string $filterexternalIdeq - */ - public function getCustomers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response - { - return $this->connector->send(new GetCustomers($filterexternalId, $filterexternalIdeq)); - } - - - public function postCustomers(): Response - { - return $this->connector->send(new PostCustomers()); - } - - - /** - * @param string $customer - */ - public function getCustomer(string $customer): Response - { - return $this->connector->send(new GetCustomer($customer)); - } - - - /** - * @param string $customer - */ - public function putCustomer(string $customer): Response - { - return $this->connector->send(new PutCustomer($customer)); - } - - - /** - * @param string $customer - */ - public function deleteCustomer(string $customer): Response - { - return $this->connector->send(new DeleteCustomer($customer)); - } - - - /** - * @param string $customer - */ - public function patchCustomer(string $customer): Response - { - return $this->connector->send(new PatchCustomer($customer)); - } + public function getCustomers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response + { + return $this->connector->send(new GetCustomers($filterexternalId, $filterexternalIdeq)); + } + + public function postCustomers(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostCustomers($data)); + } + + public function getCustomer(string $customer): Response + { + return $this->connector->send(new GetCustomer($customer)); + } + + public function putCustomer(string $customer, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PutCustomer($customer, $data)); + } + + public function deleteCustomer(string $customer): Response + { + return $this->connector->send(new DeleteCustomer($customer)); + } + + public function patchCustomer(string $customer, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PatchCustomer($customer, $data)); + } } diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 4a03d14..7f8cd7c 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -13,189 +13,101 @@ class Entry extends BaseResource { - /** - * @param string $filteruserId - * @param string $filteruserIdeq - * @param string $filteruserIdnq - * @param string $filteruserIdgt - * @param string $filteruserIdlt - * @param string $filteruserIdgte - * @param string $filteruserIdlte - * @param string $filteruserIdcontains - * @param string $filterbudgetId - * @param string $filterbudgetIdeq - * @param string $filterbudgetIdnq - * @param string $filterbudgetIdgt - * @param string $filterbudgetIdlt - * @param string $filterbudgetIdgte - * @param string $filterbudgetIdlte - * @param string $filterbudgetIdcontains - * @param string $filterstartedAt - * @param string $filterstartedAteq - * @param string $filterstartedAtnq - * @param string $filterstartedAtgt - * @param string $filterstartedAtlt - * @param string $filterstartedAtgte - * @param string $filterstartedAtlte - * @param string $filterstartedAtcontains - * @param string $filterendedAt - * @param string $filterendedAteq - * @param string $filterendedAtnq - * @param string $filterendedAtgt - * @param string $filterendedAtlt - * @param string $filterendedAtgte - * @param string $filterendedAtlte - * @param string $filterendedAtcontains - * @param string $filterhasOvertime - * @param string $filterhasOvertimeeq - * @param string $filterhasOvertimenq - * @param string $filterhasOvertimegt - * @param string $filterhasOvertimelt - * @param string $filterhasOvertimegte - * @param string $filterhasOvertimelte - * @param string $filterhasOvertimecontains - * @param string $filteruserFullName - * @param string $filteruserFullNameeq - * @param string $filteruserFullNamenq - * @param string $filteruserFullNamegt - * @param string $filteruserFullNamelt - * @param string $filteruserFullNamegte - * @param string $filteruserFullNamelte - * @param string $filteruserFullNamecontains - * @param string $filtercustomerId - * @param string $filtercustomerIdeq - * @param string $filtercustomerIdnq - * @param string $filtercustomerIdgt - * @param string $filtercustomerIdlt - * @param string $filtercustomerIdgte - * @param string $filtercustomerIdlte - * @param string $filtercustomerIdcontains - * @param string $filterticketNumber - * @param string $filterticketNumbereq - * @param string $filterticketNumbernq - * @param string $filterticketNumbergt - * @param string $filterticketNumberlt - * @param string $filterticketNumbergte - * @param string $filterticketNumberlte - * @param string $filterticketNumbercontains - * @param string $filtersettlement - * @param string $filterisInvoiced - * @param string $filterisInvoiceable - * @param string $include - */ - public function getEntries( - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ?string $filteruserIdnq = null, - ?string $filteruserIdgt = null, - ?string $filteruserIdlt = null, - ?string $filteruserIdgte = null, - ?string $filteruserIdlte = null, - ?string $filteruserIdcontains = null, - ?string $filterbudgetId = null, - ?string $filterbudgetIdeq = null, - ?string $filterbudgetIdnq = null, - ?string $filterbudgetIdgt = null, - ?string $filterbudgetIdlt = null, - ?string $filterbudgetIdgte = null, - ?string $filterbudgetIdlte = null, - ?string $filterbudgetIdcontains = null, - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterhasOvertime = null, - ?string $filterhasOvertimeeq = null, - ?string $filterhasOvertimenq = null, - ?string $filterhasOvertimegt = null, - ?string $filterhasOvertimelt = null, - ?string $filterhasOvertimegte = null, - ?string $filterhasOvertimelte = null, - ?string $filterhasOvertimecontains = null, - ?string $filteruserFullName = null, - ?string $filteruserFullNameeq = null, - ?string $filteruserFullNamenq = null, - ?string $filteruserFullNamegt = null, - ?string $filteruserFullNamelt = null, - ?string $filteruserFullNamegte = null, - ?string $filteruserFullNamelte = null, - ?string $filteruserFullNamecontains = null, - ?string $filtercustomerId = null, - ?string $filtercustomerIdeq = null, - ?string $filtercustomerIdnq = null, - ?string $filtercustomerIdgt = null, - ?string $filtercustomerIdlt = null, - ?string $filtercustomerIdgte = null, - ?string $filtercustomerIdlte = null, - ?string $filtercustomerIdcontains = null, - ?string $filterticketNumber = null, - ?string $filterticketNumbereq = null, - ?string $filterticketNumbernq = null, - ?string $filterticketNumbergt = null, - ?string $filterticketNumberlt = null, - ?string $filterticketNumbergte = null, - ?string $filterticketNumberlte = null, - ?string $filterticketNumbercontains = null, - ?string $filtersettlement = null, - ?string $filterisInvoiced = null, - ?string $filterisInvoiceable = null, - ?string $include = null, - ): Response - { - return $this->connector->send(new GetEntries($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); - } + public function getEntries( + ?string $filteruserId = null, + ?string $filteruserIdeq = null, + ?string $filteruserIdnq = null, + ?string $filteruserIdgt = null, + ?string $filteruserIdlt = null, + ?string $filteruserIdgte = null, + ?string $filteruserIdlte = null, + ?string $filteruserIdcontains = null, + ?string $filterbudgetId = null, + ?string $filterbudgetIdeq = null, + ?string $filterbudgetIdnq = null, + ?string $filterbudgetIdgt = null, + ?string $filterbudgetIdlt = null, + ?string $filterbudgetIdgte = null, + ?string $filterbudgetIdlte = null, + ?string $filterbudgetIdcontains = null, + ?string $filterstartedAt = null, + ?string $filterstartedAteq = null, + ?string $filterstartedAtnq = null, + ?string $filterstartedAtgt = null, + ?string $filterstartedAtlt = null, + ?string $filterstartedAtgte = null, + ?string $filterstartedAtlte = null, + ?string $filterstartedAtcontains = null, + ?string $filterendedAt = null, + ?string $filterendedAteq = null, + ?string $filterendedAtnq = null, + ?string $filterendedAtgt = null, + ?string $filterendedAtlt = null, + ?string $filterendedAtgte = null, + ?string $filterendedAtlte = null, + ?string $filterendedAtcontains = null, + ?string $filterhasOvertime = null, + ?string $filterhasOvertimeeq = null, + ?string $filterhasOvertimenq = null, + ?string $filterhasOvertimegt = null, + ?string $filterhasOvertimelt = null, + ?string $filterhasOvertimegte = null, + ?string $filterhasOvertimelte = null, + ?string $filterhasOvertimecontains = null, + ?string $filteruserFullName = null, + ?string $filteruserFullNameeq = null, + ?string $filteruserFullNamenq = null, + ?string $filteruserFullNamegt = null, + ?string $filteruserFullNamelt = null, + ?string $filteruserFullNamegte = null, + ?string $filteruserFullNamelte = null, + ?string $filteruserFullNamecontains = null, + ?string $filtercustomerId = null, + ?string $filtercustomerIdeq = null, + ?string $filtercustomerIdnq = null, + ?string $filtercustomerIdgt = null, + ?string $filtercustomerIdlt = null, + ?string $filtercustomerIdgte = null, + ?string $filtercustomerIdlte = null, + ?string $filtercustomerIdcontains = null, + ?string $filterticketNumber = null, + ?string $filterticketNumbereq = null, + ?string $filterticketNumbernq = null, + ?string $filterticketNumbergt = null, + ?string $filterticketNumberlt = null, + ?string $filterticketNumbergte = null, + ?string $filterticketNumberlte = null, + ?string $filterticketNumbercontains = null, + ?string $filtersettlement = null, + ?string $filterisInvoiced = null, + ?string $filterisInvoiceable = null, + ?string $include = null, + ): Response { + return $this->connector->send(new GetEntries($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + } + public function postEntries(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostEntries($data)); + } - public function postEntries(): Response - { - return $this->connector->send(new PostEntries()); - } + public function getEntry(string $entry): Response + { + return $this->connector->send(new GetEntry($entry)); + } + public function putEntry(string $entry, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PutEntry($entry, $data)); + } - /** - * @param string $entry - */ - public function getEntry(string $entry): Response - { - return $this->connector->send(new GetEntry($entry)); - } + public function deleteEntry(string $entry): Response + { + return $this->connector->send(new DeleteEntry($entry)); + } - - /** - * @param string $entry - */ - public function putEntry(string $entry): Response - { - return $this->connector->send(new PutEntry($entry)); - } - - - /** - * @param string $entry - */ - public function deleteEntry(string $entry): Response - { - return $this->connector->send(new DeleteEntry($entry)); - } - - - /** - * @param string $entry - */ - public function patchEntry(string $entry): Response - { - return $this->connector->send(new PatchEntry($entry)); - } + public function patchEntry(string $entry, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PatchEntry($entry, $data)); + } } diff --git a/src/Resource/Event.php b/src/Resource/Event.php index d898701..7b8f785 100644 --- a/src/Resource/Event.php +++ b/src/Resource/Event.php @@ -8,8 +8,8 @@ class Event extends BaseResource { - public function postEvents(): Response - { - return $this->connector->send(new PostEvents()); - } + public function postEvents(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostEvents($data)); + } } diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php index 3816e37..7bd6f26 100644 --- a/src/Resource/MarkAsExported.php +++ b/src/Resource/MarkAsExported.php @@ -8,11 +8,10 @@ class MarkAsExported extends BaseResource { - /** - * @param string $overtime - */ - public function postOvertimeMarkAsExported(string $overtime): Response - { - return $this->connector->send(new PostOvertimeMarkAsExported($overtime)); - } + public function postOvertimeMarkAsExported( + string $overtime, + \Timatic\SDK\Foundation\Model|array|null $data = null, + ): Response { + return $this->connector->send(new PostOvertimeMarkAsExported($overtime, $data)); + } } diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php index 6a3fa48..d9b6076 100644 --- a/src/Resource/MarkAsInvoiced.php +++ b/src/Resource/MarkAsInvoiced.php @@ -8,11 +8,10 @@ class MarkAsInvoiced extends BaseResource { - /** - * @param string $entry - */ - public function postEntryMarkAsInvoiced(string $entry): Response - { - return $this->connector->send(new PostEntryMarkAsInvoiced($entry)); - } + public function postEntryMarkAsInvoiced( + string $entry, + \Timatic\SDK\Foundation\Model|array|null $data = null, + ): Response { + return $this->connector->send(new PostEntryMarkAsInvoiced($entry, $data)); + } } diff --git a/src/Resource/Team.php b/src/Resource/Team.php index 6e72ccf..e79539a 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -13,50 +13,33 @@ class Team extends BaseResource { - public function getTeams(): Response - { - return $this->connector->send(new GetTeams()); - } - - - public function postTeams(): Response - { - return $this->connector->send(new PostTeams()); - } - - - /** - * @param string $team - */ - public function getTeam(string $team): Response - { - return $this->connector->send(new GetTeam($team)); - } - - - /** - * @param string $team - */ - public function putTeam(string $team): Response - { - return $this->connector->send(new PutTeam($team)); - } - - - /** - * @param string $team - */ - public function deleteTeam(string $team): Response - { - return $this->connector->send(new DeleteTeam($team)); - } - - - /** - * @param string $team - */ - public function patchTeam(string $team): Response - { - return $this->connector->send(new PatchTeam($team)); - } + public function getTeams(): Response + { + return $this->connector->send(new GetTeams); + } + + public function postTeams(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostTeams($data)); + } + + public function getTeam(string $team): Response + { + return $this->connector->send(new GetTeam($team)); + } + + public function putTeam(string $team, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PutTeam($team, $data)); + } + + public function deleteTeam(string $team): Response + { + return $this->connector->send(new DeleteTeam($team)); + } + + public function patchTeam(string $team, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PatchTeam($team, $data)); + } } diff --git a/src/Resource/User.php b/src/Resource/User.php index af3cbcb..2a03277 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -13,54 +13,33 @@ class User extends BaseResource { - /** - * @param string $filterexternalId - * @param string $filterexternalIdeq - */ - public function getUsers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response - { - return $this->connector->send(new GetUsers($filterexternalId, $filterexternalIdeq)); - } - - - public function postUsers(): Response - { - return $this->connector->send(new PostUsers()); - } - - - /** - * @param string $user - */ - public function getUser(string $user): Response - { - return $this->connector->send(new GetUser($user)); - } - - - /** - * @param string $user - */ - public function putUser(string $user): Response - { - return $this->connector->send(new PutUser($user)); - } - - - /** - * @param string $user - */ - public function deleteUser(string $user): Response - { - return $this->connector->send(new DeleteUser($user)); - } - - - /** - * @param string $user - */ - public function patchUser(string $user): Response - { - return $this->connector->send(new PatchUser($user)); - } + public function getUsers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response + { + return $this->connector->send(new GetUsers($filterexternalId, $filterexternalIdeq)); + } + + public function postUsers(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PostUsers($data)); + } + + public function getUser(string $user): Response + { + return $this->connector->send(new GetUser($user)); + } + + public function putUser(string $user, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PutUser($user, $data)); + } + + public function deleteUser(string $user): Response + { + return $this->connector->send(new DeleteUser($user)); + } + + public function patchUser(string $user, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + { + return $this->connector->send(new PatchUser($user, $data)); + } } From e4b7be98536f5959c4b976f0ba4aebc7249f23ba Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 09:22:14 +0100 Subject: [PATCH 10/62] run generate and pint --- CLAUDE.md | 54 ++- README.md | 69 +++- composer.json | 7 +- generator/JsonApiDtoGenerator.php | 5 +- generator/JsonApiPestTestGenerator.php | 6 +- generator/generate.php | 6 - regenerate-sdk.sh | 20 - src/Attributes/DateTime.php | 3 +- src/Attributes/Property.php | 3 +- src/Dto/Approve.php | 40 +- src/Dto/Budget.php | 72 ++-- src/Dto/BudgetTimeSpentTotal.php | 24 +- src/Dto/BudgetType.php | 36 +- src/Dto/Correction.php | 12 +- src/Dto/Customer.php | 28 +- src/Dto/DailyProgress.php | 14 +- src/Dto/Entry.php | 108 ++--- src/Dto/EntrySuggestion.php | 40 +- src/Dto/Event.php | 60 +-- src/Dto/ExportMail.php | 5 +- src/Dto/MarkAsExported.php | 40 +- src/Dto/MarkAsInvoiced.php | 108 ++--- src/Dto/Overtime.php | 40 +- src/Dto/Team.php | 20 +- src/Dto/TimeSpentTotal.php | 28 +- src/Dto/User.php | 8 +- src/Dto/UserCustomerHoursAggregate.php | 20 +- src/Foundation/HasAttributes.php | 2 +- src/Foundation/Model.php | 2 +- src/Requests/Approve/PostOvertimeApprove.php | 48 +-- src/Requests/Budget/DeleteBudget.php | 24 +- src/Requests/Budget/GetBudget.php | 24 +- src/Requests/Budget/PatchBudget.php | 48 +-- src/Requests/Budget/PostBudgets.php | 35 +- src/Requests/Budget/PutBudget.php | 44 +- src/Requests/Change/GetChange.php | 24 +- src/Requests/Correction/PatchCorrection.php | 48 +-- src/Requests/Correction/PostCorrections.php | 35 +- src/Requests/Correction/PutCorrection.php | 44 +- src/Requests/Customer/DeleteCustomer.php | 24 +- src/Requests/Customer/GetCustomer.php | 24 +- src/Requests/Customer/PatchCustomer.php | 48 +-- src/Requests/Customer/PostCustomers.php | 35 +- src/Requests/Customer/PutCustomer.php | 44 +- .../EntriesExport/GetBudgetEntriesExport.php | 375 +++++++----------- src/Requests/Entry/DeleteEntry.php | 24 +- src/Requests/Entry/GetEntry.php | 24 +- src/Requests/Entry/PatchEntry.php | 48 +-- src/Requests/Entry/PostEntries.php | 35 +- src/Requests/Entry/PutEntry.php | 44 +- .../EntrySuggestion/DeleteEntrySuggestion.php | 24 +- .../EntrySuggestion/GetEntrySuggestion.php | 24 +- src/Requests/Event/PostEvents.php | 35 +- src/Requests/Incident/GetIncident.php | 24 +- .../PostOvertimeMarkAsExported.php | 48 +-- .../PostEntryMarkAsInvoiced.php | 48 +-- src/Requests/Number/GetIncidentsNumber.php | 24 +- src/Requests/Period/GetBudgetPeriods.php | 24 +- src/Requests/Team/DeleteTeam.php | 24 +- src/Requests/Team/GetTeam.php | 24 +- src/Requests/Team/PatchTeam.php | 48 +-- src/Requests/Team/PostTeams.php | 35 +- src/Requests/Team/PutTeam.php | 44 +- src/Requests/User/DeleteUser.php | 24 +- src/Requests/User/GetUser.php | 24 +- src/Requests/User/PatchUser.php | 48 +-- src/Requests/User/PostUsers.php | 35 +- src/Requests/User/PutUser.php | 44 +- src/Resource/BudgetTimeSpentTotal.php | 12 +- src/Resource/BudgetType.php | 8 +- src/Resource/Change.php | 20 +- src/Resource/DailyProgress.php | 8 +- src/Resource/EntriesExport.php | 218 ++++------ src/Resource/EntrySuggestion.php | 59 +-- src/Resource/ExportMail.php | 8 +- src/Resource/Incident.php | 20 +- src/Resource/Me.php | 8 +- src/Resource/Number.php | 11 +- src/Resource/Overtime.php | 89 ++--- src/Resource/Period.php | 11 +- src/Resource/TimeSpentTotal.php | 29 +- src/Resource/UserCustomerHoursAggregate.php | 107 ++--- tests/JsonApiBodyTest.php | 6 +- 83 files changed, 1355 insertions(+), 1839 deletions(-) delete mode 100755 regenerate-sdk.sh diff --git a/CLAUDE.md b/CLAUDE.md index 3b85cbf..d53e9dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -295,36 +295,53 @@ This SDK is automatically generated from the OpenAPI specification. **DO NOT man To update auto-generated files from the latest OpenAPI spec: ```bash -# 1. Run the generator -php generator/generate.php +# Run the regenerate script (handles all steps) +composer regenerate -# 2. IMMEDIATELY restore TimaticConnector (contains custom methods) +# IMPORTANT: Restore TimaticConnector (contains custom methods) git checkout src/TimaticConnector.php -# 3. Update autoloader -composer dump-autoload +# Verify with tests +composer test +``` -# 4. Run tests to verify -./vendor/bin/pest +**What `composer regenerate` does:** +1. Runs `php generator/generate.php` to download OpenAPI spec and generate files +2. Updates autoloader with `composer dump-autoload` +3. Formats all code with Laravel Pint -# 5. Format code -./vendor/bin/pint -``` +**Note:** You MUST run `git checkout src/TimaticConnector.php` after regeneration to restore custom configuration methods. ## Modifying Auto-Generated Code -If you need to change how files are generated, update the generators: +If you need to change how files are generated, update the appropriate generator: + +### Generator Mapping per Directory: + +| Directory | Generator | Description | +|-----------|-----------|-------------| +| `src/Dto/` | `generator/JsonApiDtoGenerator.php` | Generates Models from OpenAPI schemas. Flattens JSON:API attributes into typed properties. Adds `#[Property]` and `#[DateTime]` attributes. | +| `src/Requests/` | `generator/JsonApiRequestGenerator.php` | Generates Request classes. Adds Model support for POST/PUT/PATCH. Adds `Paginatable` interface to GET collections. | +| `src/Resource/` | `generator/generate.php` (post-processing) | Resources are generated by SDK generator, then post-processed in generate.php to add `$data` parameters to POST/PUT/PATCH methods. | -- **`generator/JsonApiDtoGenerator.php`** - Customizes Model/DTO generation - - Flattens JSON:API attributes into typed properties - - Adds `#[Property]` and `#[DateTime]` attributes +### Generator Details: -- **`generator/JsonApiRequestGenerator.php`** - Customizes Request generation - - Adds Model support for POST/PUT/PATCH - - Adds `Paginatable` interface to GET collection requests +- **`generator/JsonApiDtoGenerator.php`** - Model/DTO generation + - Extends `Model` instead of Spatie Data + - Flattens JSON:API `attributes` object into direct properties + - Adds `#[Property]` attribute to all properties + - Converts `format: date-time` fields to Carbon instances with `#[DateTime]` attribute + +- **`generator/JsonApiRequestGenerator.php`** - Request class generation + - Adds `Model|array $data` parameter to POST/PUT/PATCH requests (when no body parameters exist) + - Adds `Paginatable` interface to GET collection requests (requests without path parameters) - Generates `defaultBody()` method for JSON:API serialization -- **`generator/generate.php`** - Main generation script +- **`generator/generate.php`** - Main generation script + Resource post-processing + - Downloads OpenAPI spec from API + - Runs code generation with custom generators + - **Post-processes Resource classes**: Adds `$data` parameter to POST/PUT/PATCH methods + - Writes all generated files After modifying generators, regenerate and test: ```bash @@ -332,4 +349,5 @@ php generator/generate.php git checkout src/TimaticConnector.php composer dump-autoload ./vendor/bin/pest +./vendor/bin/pint ``` diff --git a/README.md b/README.md index 1d72040..2bbade5 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,72 @@ class SyncBudgetsCommand extends Command } ``` +### Pagination + +The SDK supports automatic pagination for all collection endpoints using Saloon's pagination plugin: + +```php +use Timatic\SDK\TimaticConnector; +use Timatic\SDK\Requests\Budget\GetBudgets; + +class BudgetController extends Controller +{ + public function index(TimaticConnector $timatic) + { + // Create a paginator + $paginator = $timatic->paginate(new GetBudgets()); + + // Optionally set items per page (default is API's default) + $paginator->setPerPageLimit(50); + + // Iterate through all pages automatically + foreach ($paginator->items() as $budget) { + // Process each budget across all pages + // The paginator handles pagination automatically + } + + // Or collect all items at once + $allBudgets = $paginator->collect(); + } +} +``` + +The paginator: +- Automatically handles JSON:API pagination (`page[number]` and `page[size]`) +- Detects the last page via `links.next` +- Works with all GET collection requests (GetBudgets, GetCustomers, GetUsers, etc.) + +### Custom Response Methods + +All responses are instances of `TimaticResponse` which extends Saloon's Response with JSON:API convenience methods: + +```php +$response = $timatic->budget()->getBudgets(); + +// Get the first item from a collection +$firstBudget = $response->firstItem(); + +// Check for errors +if ($response->hasErrors()) { + $errors = $response->errors(); + // Handle errors... +} + +// Access JSON:API meta information +$meta = $response->meta(); +$total = $meta['total'] ?? 0; + +// Access pagination links +$links = $response->links(); +$nextPage = $links['next'] ?? null; + +// Access included resources +$included = $response->included(); +foreach ($included as $resource) { + // Process related resources +} +``` + ## Available Resources The SDK provides access to the following resources: @@ -141,13 +207,14 @@ $budget->startedAt; // Carbon instance for datetime fields This SDK is automatically generated from the Timatic API OpenAPI specification using a custom JSON:API generator. To regenerate the SDK with the latest API changes: ```bash -./regenerate-sdk.sh +composer regenerate ``` This will: 1. Download the latest OpenAPI specification from the API 2. Generate Models with flattened JSON:API attributes 3. Update the autoloader +4. Format the code with Laravel Pint ### How It Works diff --git a/composer.json b/composer.json index e809d47..ab48279 100644 --- a/composer.json +++ b/composer.json @@ -32,7 +32,12 @@ "scripts": { "test": "pest", "format": "pint", - "analyse": "phpstan analyse --memory-limit=2G" + "analyse": "phpstan analyse --memory-limit=2G", + "regenerate": [ + "php generator/generate.php", + "@composer dump-autoload", + "@format" + ] }, "extra": { "laravel": { diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php index 2171342..eda5631 100644 --- a/generator/JsonApiDtoGenerator.php +++ b/generator/JsonApiDtoGenerator.php @@ -12,7 +12,6 @@ use Crescat\SaloonSdkGenerator\Helpers\Utils; use Illuminate\Support\Str; use Nette\PhpGenerator\ClassType; -use Nette\PhpGenerator\Literal; use Nette\PhpGenerator\PhpFile; use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; @@ -38,7 +37,7 @@ protected function generateModelClass(string $className, Schema $schema): PhpFil $modelName = NameHelper::dtoClassName($className ?: $this->config->fallbackResourceName); $classType = new ClassType($modelName); - $classFile = new PhpFile(); + $classFile = new PhpFile; $namespace = $classFile ->addNamespace("{$this->config->namespace}\\{$this->config->dtoNamespaceSuffix}"); @@ -112,7 +111,7 @@ protected function addPropertyToClass( $namespace->addUse(DateTime::class); // Change type to Carbon if datetime - if (!str_contains($type, 'Carbon')) { + if (! str_contains($type, 'Carbon')) { $property->setType('null|\\Carbon\\Carbon'); } } diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index e24dc21..aa350d0 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -59,9 +59,9 @@ public function generateTest(string $resourceName, array $endpoints): PhpFile|Ta } // Add Saloon testing imports - $imports[] = "use Saloon\\Http\\Faking\\MockClient;"; - $imports[] = "use Saloon\\Http\\Faking\\MockResponse;"; - $imports[] = "use Saloon\\Http\\Request;"; + $imports[] = 'use Saloon\\Http\\Faking\\MockClient;'; + $imports[] = 'use Saloon\\Http\\Faking\\MockResponse;'; + $imports[] = 'use Saloon\\Http\\Request;'; $fileStub = str_replace('{{ requestImports }}', implode("\n", $imports), $fileStub); diff --git a/generator/generate.php b/generator/generate.php index 3511805..e052c36 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -165,9 +165,3 @@ function writeFile($file, $outputDir, $namespace) } } } - -echo "\nāœ… SDK generation complete!\n"; -echo "\nšŸ’” Next steps:\n"; -echo " 1. Run 'composer dump-autoload'\n"; -echo " 2. Review generated models in src/Dto/\n"; -echo " 3. Run tests with './vendor/bin/pest'\n"; diff --git a/regenerate-sdk.sh b/regenerate-sdk.sh deleted file mode 100755 index 005d75a..0000000 --- a/regenerate-sdk.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/bash - -# Timatic PHP SDK Regeneration Script -# This script downloads the latest OpenAPI specification and regenerates the SDK - -set -e - -echo "šŸ”„ Regenerating Timatic PHP SDK with JSON:API support..." -echo "" - -# Run the custom generator -php generator/generate.php - -# Update autoloader -echo "" -echo "šŸ“¦ Updating autoloader..." -composer dump-autoload - -echo "" -echo "āœ… All done! SDK has been regenerated with JSON:API models." diff --git a/src/Attributes/DateTime.php b/src/Attributes/DateTime.php index 0ff908c..17ea6b9 100644 --- a/src/Attributes/DateTime.php +++ b/src/Attributes/DateTime.php @@ -11,6 +11,5 @@ class DateTime { public function __construct( public string $format = 'Y-m-d\TH:i:sP' - ) { - } + ) {} } diff --git a/src/Attributes/Property.php b/src/Attributes/Property.php index c917d39..c748868 100644 --- a/src/Attributes/Property.php +++ b/src/Attributes/Property.php @@ -11,6 +11,5 @@ class Property { public function __construct( public bool $isReadOnly = false - ) { - } + ) {} } diff --git a/src/Dto/Approve.php b/src/Dto/Approve.php index c9a32fd..c64e5c3 100644 --- a/src/Dto/Approve.php +++ b/src/Dto/Approve.php @@ -7,33 +7,33 @@ class Approve extends Model { - #[Property] - public ?string $entryId; + #[Property] + public ?string $entryId; - #[Property] - public ?string $overtimeTypeId; + #[Property] + public ?string $overtimeTypeId; - #[Property] - public ?string $startedAt; + #[Property] + public ?string $startedAt; - #[Property] - public ?string $endedAt; + #[Property] + public ?string $endedAt; - #[Property] - public ?string $percentages; + #[Property] + public ?string $percentages; - #[Property] - public ?string $approvedAt; + #[Property] + public ?string $approvedAt; - #[Property] - public ?string $approvedByUserId; + #[Property] + public ?string $approvedByUserId; - #[Property] - public ?string $exportedAt; + #[Property] + public ?string $exportedAt; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; } diff --git a/src/Dto/Budget.php b/src/Dto/Budget.php index f696014..b377d81 100644 --- a/src/Dto/Budget.php +++ b/src/Dto/Budget.php @@ -8,55 +8,55 @@ class Budget extends Model { - #[Property] - public ?string $budgetTypeId; + #[Property] + public ?string $budgetTypeId; - #[Property] - public ?string $customerId; + #[Property] + public ?string $customerId; - #[Property] - public ?bool $showToCustomer; + #[Property] + public ?bool $showToCustomer; - #[Property] - public ?string $changeId; + #[Property] + public ?string $changeId; - #[Property] - public ?string $contractId; + #[Property] + public ?string $contractId; - #[Property] - public ?string $title; + #[Property] + public ?string $title; - #[Property] - public ?string $description; + #[Property] + public ?string $description; - #[Property] - public ?string $totalPrice; + #[Property] + public ?string $totalPrice; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $startedAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $startedAt; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $endedAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $endedAt; - #[Property] - public ?int $initialMinutes; + #[Property] + public ?int $initialMinutes; - #[Property] - public ?bool $isArchived; + #[Property] + public ?bool $isArchived; - #[Property] - public ?string $renewalFrequency; + #[Property] + public ?string $renewalFrequency; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $createdAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $createdAt; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $updatedAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $updatedAt; - #[Property] - public ?string $supervisorUserId; + #[Property] + public ?string $supervisorUserId; } diff --git a/src/Dto/BudgetTimeSpentTotal.php b/src/Dto/BudgetTimeSpentTotal.php index cb292de..b35d22e 100644 --- a/src/Dto/BudgetTimeSpentTotal.php +++ b/src/Dto/BudgetTimeSpentTotal.php @@ -8,20 +8,20 @@ class BudgetTimeSpentTotal extends Model { - #[Property] - #[DateTime] - public null|\Carbon\Carbon $start; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $start; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $end; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $end; - #[Property] - public ?int $remainingMinutes; + #[Property] + public ?int $remainingMinutes; - #[Property] - public ?string $periodUnit; + #[Property] + public ?string $periodUnit; - #[Property] - public ?int $periodValue; + #[Property] + public ?int $periodValue; } diff --git a/src/Dto/BudgetType.php b/src/Dto/BudgetType.php index 1ab2e55..aac2c09 100644 --- a/src/Dto/BudgetType.php +++ b/src/Dto/BudgetType.php @@ -7,30 +7,30 @@ class BudgetType extends Model { - #[Property] - public ?string $title; + #[Property] + public ?string $title; - #[Property] - public ?bool $isArchived; + #[Property] + public ?bool $isArchived; - #[Property] - public ?bool $hasChangeTicket; + #[Property] + public ?bool $hasChangeTicket; - #[Property] - public ?string $renewalFrequencies; + #[Property] + public ?string $renewalFrequencies; - #[Property] - public ?bool $hasSupervisor; + #[Property] + public ?bool $hasSupervisor; - #[Property] - public ?bool $hasContractId; + #[Property] + public ?bool $hasContractId; - #[Property] - public ?bool $hasTotalPrice; + #[Property] + public ?bool $hasTotalPrice; - #[Property] - public ?bool $ticketIsRequired; + #[Property] + public ?bool $ticketIsRequired; - #[Property] - public ?string $defaultTitle; + #[Property] + public ?string $defaultTitle; } diff --git a/src/Dto/Correction.php b/src/Dto/Correction.php index 38cb116..8a40385 100644 --- a/src/Dto/Correction.php +++ b/src/Dto/Correction.php @@ -8,11 +8,11 @@ class Correction extends Model { - #[Property] - #[DateTime] - public null|\Carbon\Carbon $createdAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $createdAt; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $updatedAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $updatedAt; } diff --git a/src/Dto/Customer.php b/src/Dto/Customer.php index fb5c347..31dc3c3 100644 --- a/src/Dto/Customer.php +++ b/src/Dto/Customer.php @@ -8,23 +8,23 @@ class Customer extends Model { - #[Property] - public ?string $externalId; + #[Property] + public ?string $externalId; - #[Property] - public ?string $name; + #[Property] + public ?string $name; - #[Property] - public ?string $hourlyRate; + #[Property] + public ?string $hourlyRate; - #[Property] - public ?string $accountManagerUserId; + #[Property] + public ?string $accountManagerUserId; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $createdAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $createdAt; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $updatedAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $updatedAt; } diff --git a/src/Dto/DailyProgress.php b/src/Dto/DailyProgress.php index f69e4c7..d29fa3c 100644 --- a/src/Dto/DailyProgress.php +++ b/src/Dto/DailyProgress.php @@ -8,13 +8,13 @@ class DailyProgress extends Model { - #[Property] - public ?string $userId; + #[Property] + public ?string $userId; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $date; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $date; - #[Property] - public ?string $progress; + #[Property] + public ?string $progress; } diff --git a/src/Dto/Entry.php b/src/Dto/Entry.php index d82d833..17db8df 100644 --- a/src/Dto/Entry.php +++ b/src/Dto/Entry.php @@ -7,84 +7,84 @@ class Entry extends Model { - #[Property] - public ?string $ticketId; + #[Property] + public ?string $ticketId; - #[Property] - public ?string $ticketNumber; + #[Property] + public ?string $ticketNumber; - #[Property] - public ?string $ticketTitle; + #[Property] + public ?string $ticketTitle; - #[Property] - public ?string $ticketType; + #[Property] + public ?string $ticketType; - #[Property] - public ?string $customerId; + #[Property] + public ?string $customerId; - #[Property] - public ?string $customerName; + #[Property] + public ?string $customerName; - #[Property] - public ?string $hourlyRate; + #[Property] + public ?string $hourlyRate; - #[Property] - public ?bool $hadEmergencyShift; + #[Property] + public ?bool $hadEmergencyShift; - #[Property] - public ?string $budgetId; + #[Property] + public ?string $budgetId; - #[Property] - public ?bool $isPaidPerHour; + #[Property] + public ?bool $isPaidPerHour; - #[Property] - public ?int $minutesSpent; + #[Property] + public ?int $minutesSpent; - #[Property] - public ?string $userId; + #[Property] + public ?string $userId; - #[Property] - public ?string $userEmail; + #[Property] + public ?string $userEmail; - #[Property] - public ?string $userFullName; + #[Property] + public ?string $userFullName; - #[Property] - public ?string $createdByUserId; + #[Property] + public ?string $createdByUserId; - #[Property] - public ?string $createdByUserEmail; + #[Property] + public ?string $createdByUserEmail; - #[Property] - public ?string $createdByUserFullName; + #[Property] + public ?string $createdByUserFullName; - #[Property] - public ?string $entryType; + #[Property] + public ?string $entryType; - #[Property] - public ?string $description; + #[Property] + public ?string $description; - #[Property] - public ?bool $isInternal; + #[Property] + public ?bool $isInternal; - #[Property] - public ?string $startedAt; + #[Property] + public ?string $startedAt; - #[Property] - public ?string $endedAt; + #[Property] + public ?string $endedAt; - #[Property] - public ?string $invoicedAt; + #[Property] + public ?string $invoicedAt; - #[Property] - public ?string $isInvoiced; + #[Property] + public ?string $isInvoiced; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; - #[Property] - public ?bool $isBasedOnSuggestion; + #[Property] + public ?bool $isBasedOnSuggestion; } diff --git a/src/Dto/EntrySuggestion.php b/src/Dto/EntrySuggestion.php index 8209501..5b3030c 100644 --- a/src/Dto/EntrySuggestion.php +++ b/src/Dto/EntrySuggestion.php @@ -7,33 +7,33 @@ class EntrySuggestion extends Model { - #[Property] - public ?string $ticketId; + #[Property] + public ?string $ticketId; - #[Property] - public ?string $ticketNumber; + #[Property] + public ?string $ticketNumber; - #[Property] - public ?string $customerId; + #[Property] + public ?string $customerId; - #[Property] - public ?string $userId; + #[Property] + public ?string $userId; - #[Property] - public ?string $date; + #[Property] + public ?string $date; - #[Property] - public ?string $ticketTitle; + #[Property] + public ?string $ticketTitle; - #[Property] - public ?string $ticketType; + #[Property] + public ?string $ticketType; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; - #[Property] - public ?string $budgetId; + #[Property] + public ?string $budgetId; } diff --git a/src/Dto/Event.php b/src/Dto/Event.php index 850ec28..d883d0d 100644 --- a/src/Dto/Event.php +++ b/src/Dto/Event.php @@ -7,48 +7,48 @@ class Event extends Model { - #[Property] - public ?string $userId; + #[Property] + public ?string $userId; - #[Property] - public ?string $budgetId; + #[Property] + public ?string $budgetId; - #[Property] - public ?string $ticketId; + #[Property] + public ?string $ticketId; - #[Property] - public ?string $sourceId; + #[Property] + public ?string $sourceId; - #[Property] - public ?string $ticketNumber; + #[Property] + public ?string $ticketNumber; - #[Property] - public ?string $ticketType; + #[Property] + public ?string $ticketType; - #[Property] - public ?string $title; + #[Property] + public ?string $title; - #[Property] - public ?string $description; + #[Property] + public ?string $description; - #[Property] - public ?string $customerId; + #[Property] + public ?string $customerId; - #[Property] - public ?string $eventTypeId; + #[Property] + public ?string $eventTypeId; - #[Property] - public ?string $startedAt; + #[Property] + public ?string $startedAt; - #[Property] - public ?string $endedAt; + #[Property] + public ?string $endedAt; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; - #[Property] - public ?string $isInternal; + #[Property] + public ?string $isInternal; } diff --git a/src/Dto/ExportMail.php b/src/Dto/ExportMail.php index 213ddab..490a2b4 100644 --- a/src/Dto/ExportMail.php +++ b/src/Dto/ExportMail.php @@ -2,9 +2,6 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Foundation\Model; -class ExportMail extends Model -{ -} +class ExportMail extends Model {} diff --git a/src/Dto/MarkAsExported.php b/src/Dto/MarkAsExported.php index 5917a3e..81da941 100644 --- a/src/Dto/MarkAsExported.php +++ b/src/Dto/MarkAsExported.php @@ -7,33 +7,33 @@ class MarkAsExported extends Model { - #[Property] - public ?string $entryId; + #[Property] + public ?string $entryId; - #[Property] - public ?string $overtimeTypeId; + #[Property] + public ?string $overtimeTypeId; - #[Property] - public ?string $startedAt; + #[Property] + public ?string $startedAt; - #[Property] - public ?string $endedAt; + #[Property] + public ?string $endedAt; - #[Property] - public ?string $percentages; + #[Property] + public ?string $percentages; - #[Property] - public ?string $approvedAt; + #[Property] + public ?string $approvedAt; - #[Property] - public ?string $approvedByUserId; + #[Property] + public ?string $approvedByUserId; - #[Property] - public ?string $exportedAt; + #[Property] + public ?string $exportedAt; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; } diff --git a/src/Dto/MarkAsInvoiced.php b/src/Dto/MarkAsInvoiced.php index d84c11a..df70d85 100644 --- a/src/Dto/MarkAsInvoiced.php +++ b/src/Dto/MarkAsInvoiced.php @@ -7,84 +7,84 @@ class MarkAsInvoiced extends Model { - #[Property] - public ?string $ticketId; + #[Property] + public ?string $ticketId; - #[Property] - public ?string $ticketNumber; + #[Property] + public ?string $ticketNumber; - #[Property] - public ?string $ticketTitle; + #[Property] + public ?string $ticketTitle; - #[Property] - public ?string $ticketType; + #[Property] + public ?string $ticketType; - #[Property] - public ?string $customerId; + #[Property] + public ?string $customerId; - #[Property] - public ?string $customerName; + #[Property] + public ?string $customerName; - #[Property] - public ?string $hourlyRate; + #[Property] + public ?string $hourlyRate; - #[Property] - public ?bool $hadEmergencyShift; + #[Property] + public ?bool $hadEmergencyShift; - #[Property] - public ?string $budgetId; + #[Property] + public ?string $budgetId; - #[Property] - public ?bool $isPaidPerHour; + #[Property] + public ?bool $isPaidPerHour; - #[Property] - public ?int $minutesSpent; + #[Property] + public ?int $minutesSpent; - #[Property] - public ?string $userId; + #[Property] + public ?string $userId; - #[Property] - public ?string $userEmail; + #[Property] + public ?string $userEmail; - #[Property] - public ?string $userFullName; + #[Property] + public ?string $userFullName; - #[Property] - public ?string $createdByUserId; + #[Property] + public ?string $createdByUserId; - #[Property] - public ?string $createdByUserEmail; + #[Property] + public ?string $createdByUserEmail; - #[Property] - public ?string $createdByUserFullName; + #[Property] + public ?string $createdByUserFullName; - #[Property] - public ?string $entryType; + #[Property] + public ?string $entryType; - #[Property] - public ?string $description; + #[Property] + public ?string $description; - #[Property] - public ?bool $isInternal; + #[Property] + public ?bool $isInternal; - #[Property] - public ?string $startedAt; + #[Property] + public ?string $startedAt; - #[Property] - public ?string $endedAt; + #[Property] + public ?string $endedAt; - #[Property] - public ?string $invoicedAt; + #[Property] + public ?string $invoicedAt; - #[Property] - public ?string $isInvoiced; + #[Property] + public ?string $isInvoiced; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; - #[Property] - public ?bool $isBasedOnSuggestion; + #[Property] + public ?bool $isBasedOnSuggestion; } diff --git a/src/Dto/Overtime.php b/src/Dto/Overtime.php index a4c0871..a148830 100644 --- a/src/Dto/Overtime.php +++ b/src/Dto/Overtime.php @@ -7,33 +7,33 @@ class Overtime extends Model { - #[Property] - public ?string $entryId; + #[Property] + public ?string $entryId; - #[Property] - public ?string $overtimeTypeId; + #[Property] + public ?string $overtimeTypeId; - #[Property] - public ?string $startedAt; + #[Property] + public ?string $startedAt; - #[Property] - public ?string $endedAt; + #[Property] + public ?string $endedAt; - #[Property] - public ?string $percentages; + #[Property] + public ?string $percentages; - #[Property] - public ?string $approvedAt; + #[Property] + public ?string $approvedAt; - #[Property] - public ?string $approvedByUserId; + #[Property] + public ?string $approvedByUserId; - #[Property] - public ?string $exportedAt; + #[Property] + public ?string $exportedAt; - #[Property] - public ?string $createdAt; + #[Property] + public ?string $createdAt; - #[Property] - public ?string $updatedAt; + #[Property] + public ?string $updatedAt; } diff --git a/src/Dto/Team.php b/src/Dto/Team.php index ab332d0..aacb600 100644 --- a/src/Dto/Team.php +++ b/src/Dto/Team.php @@ -8,17 +8,17 @@ class Team extends Model { - #[Property] - public ?string $externalId; + #[Property] + public ?string $externalId; - #[Property] - public ?string $name; + #[Property] + public ?string $name; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $createdAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $createdAt; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $updatedAt; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $updatedAt; } diff --git a/src/Dto/TimeSpentTotal.php b/src/Dto/TimeSpentTotal.php index 167b736..e5b3284 100644 --- a/src/Dto/TimeSpentTotal.php +++ b/src/Dto/TimeSpentTotal.php @@ -8,23 +8,23 @@ class TimeSpentTotal extends Model { - #[Property] - #[DateTime] - public null|\Carbon\Carbon $start; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $start; - #[Property] - #[DateTime] - public null|\Carbon\Carbon $end; + #[Property] + #[DateTime] + public ?\Carbon\Carbon $end; - #[Property] - public ?int $internalMinutes; + #[Property] + public ?int $internalMinutes; - #[Property] - public ?int $billableMinutes; + #[Property] + public ?int $billableMinutes; - #[Property] - public ?string $periodUnit; + #[Property] + public ?string $periodUnit; - #[Property] - public ?int $periodValue; + #[Property] + public ?int $periodValue; } diff --git a/src/Dto/User.php b/src/Dto/User.php index f1cf404..76cdf03 100644 --- a/src/Dto/User.php +++ b/src/Dto/User.php @@ -7,9 +7,9 @@ class User extends Model { - #[Property] - public ?string $externalId; + #[Property] + public ?string $externalId; - #[Property] - public ?string $email; + #[Property] + public ?string $email; } diff --git a/src/Dto/UserCustomerHoursAggregate.php b/src/Dto/UserCustomerHoursAggregate.php index 7d73831..79a502b 100644 --- a/src/Dto/UserCustomerHoursAggregate.php +++ b/src/Dto/UserCustomerHoursAggregate.php @@ -7,18 +7,18 @@ class UserCustomerHoursAggregate extends Model { - #[Property] - public ?string $customerId; + #[Property] + public ?string $customerId; - #[Property] - public ?string $userId; + #[Property] + public ?string $userId; - #[Property] - public ?int $internalMinutes; + #[Property] + public ?int $internalMinutes; - #[Property] - public ?int $budgetMinutes; + #[Property] + public ?int $budgetMinutes; - #[Property] - public ?int $paidPerHourMinutes; + #[Property] + public ?int $paidPerHourMinutes; } diff --git a/src/Foundation/HasAttributes.php b/src/Foundation/HasAttributes.php index 59a148a..72c7cd4 100644 --- a/src/Foundation/HasAttributes.php +++ b/src/Foundation/HasAttributes.php @@ -27,7 +27,7 @@ public function attributes(): array $propertyName = $property->getName(); // Skip if property is not initialized - if (!$property->isInitialized($this)) { + if (! $property->isInitialized($this)) { continue; } diff --git a/src/Foundation/Model.php b/src/Foundation/Model.php index 78445c3..9722181 100644 --- a/src/Foundation/Model.php +++ b/src/Foundation/Model.php @@ -15,7 +15,7 @@ abstract class Model implements ModelInterface #[Property(isReadOnly: true)] public string $id; - protected null|string $type = null; + protected ?string $type = null; /** * @param array $attributes diff --git a/src/Requests/Approve/PostOvertimeApprove.php b/src/Requests/Approve/PostOvertimeApprove.php index a77cdcc..b542c99 100644 --- a/src/Requests/Approve/PostOvertimeApprove.php +++ b/src/Requests/Approve/PostOvertimeApprove.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Approve; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PostOvertimeApprove extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::POST; - - - public function resolveEndpoint(): string - { - return "/overtimes/{$this->overtime}/approve"; - } - - - /** - * @param string $overtime - */ - public function __construct( - protected string $overtime, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::POST; + + public function resolveEndpoint(): string + { + return "/overtimes/{$this->overtime}/approve"; + } + + public function __construct( + protected string $overtime, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Budget/DeleteBudget.php b/src/Requests/Budget/DeleteBudget.php index bcf2b08..d50969f 100644 --- a/src/Requests/Budget/DeleteBudget.php +++ b/src/Requests/Budget/DeleteBudget.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Budget; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * deleteBudget */ class DeleteBudget extends Request { - protected Method $method = Method::DELETE; + protected Method $method = Method::DELETE; + public function resolveEndpoint(): string + { + return "/budgets/{$this->budget}"; + } - public function resolveEndpoint(): string - { - return "/budgets/{$this->budget}"; - } - - - /** - * @param string $budget - */ - public function __construct( - protected string $budget, - ) { - } + public function __construct( + protected string $budget, + ) {} } diff --git a/src/Requests/Budget/GetBudget.php b/src/Requests/Budget/GetBudget.php index 64ccab0..e9fb3bb 100644 --- a/src/Requests/Budget/GetBudget.php +++ b/src/Requests/Budget/GetBudget.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Budget; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getBudget */ class GetBudget extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/budgets/{$this->budget}"; + } - public function resolveEndpoint(): string - { - return "/budgets/{$this->budget}"; - } - - - /** - * @param string $budget - */ - public function __construct( - protected string $budget, - ) { - } + public function __construct( + protected string $budget, + ) {} } diff --git a/src/Requests/Budget/PatchBudget.php b/src/Requests/Budget/PatchBudget.php index aa86d28..203f428 100644 --- a/src/Requests/Budget/PatchBudget.php +++ b/src/Requests/Budget/PatchBudget.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Budget; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PatchBudget extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::PATCH; - - - public function resolveEndpoint(): string - { - return "/budgets/{$this->budget}"; - } - - - /** - * @param string $budget - */ - public function __construct( - protected string $budget, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::PATCH; + + public function resolveEndpoint(): string + { + return "/budgets/{$this->budget}"; + } + + public function __construct( + protected string $budget, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Budget/PostBudgets.php b/src/Requests/Budget/PostBudgets.php index 9806c6c..75043f6 100644 --- a/src/Requests/Budget/PostBudgets.php +++ b/src/Requests/Budget/PostBudgets.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Budget; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostBudgets extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/budgets'; + } - public function resolveEndpoint(): string - { - return "/budgets"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Budget/PutBudget.php b/src/Requests/Budget/PutBudget.php index 192d516..b4ccdbb 100644 --- a/src/Requests/Budget/PutBudget.php +++ b/src/Requests/Budget/PutBudget.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Budget; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; use Timatic\SDK\Foundation\Model; @@ -12,29 +11,22 @@ */ class PutBudget extends Request { - protected Method $method = Method::PUT; - - - public function resolveEndpoint(): string - { - return "/budgets/{$this->budget}"; - } - - - /** - * @param string $budget - */ - public function __construct( - protected string $budget, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected Method $method = Method::PUT; + + public function resolveEndpoint(): string + { + return "/budgets/{$this->budget}"; + } + + public function __construct( + protected string $budget, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Change/GetChange.php b/src/Requests/Change/GetChange.php index 65c7b86..112ccbb 100644 --- a/src/Requests/Change/GetChange.php +++ b/src/Requests/Change/GetChange.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Change; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getChange */ class GetChange extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/changes/{$this->change}"; + } - public function resolveEndpoint(): string - { - return "/changes/{$this->change}"; - } - - - /** - * @param string $change - */ - public function __construct( - protected string $change, - ) { - } + public function __construct( + protected string $change, + ) {} } diff --git a/src/Requests/Correction/PatchCorrection.php b/src/Requests/Correction/PatchCorrection.php index daa9ef1..3b206dd 100644 --- a/src/Requests/Correction/PatchCorrection.php +++ b/src/Requests/Correction/PatchCorrection.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Correction; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PatchCorrection extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::PATCH; - - - public function resolveEndpoint(): string - { - return "/corrections/{$this->correction}"; - } - - - /** - * @param string $correction - */ - public function __construct( - protected string $correction, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::PATCH; + + public function resolveEndpoint(): string + { + return "/corrections/{$this->correction}"; + } + + public function __construct( + protected string $correction, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Correction/PostCorrections.php b/src/Requests/Correction/PostCorrections.php index 82be959..63f6048 100644 --- a/src/Requests/Correction/PostCorrections.php +++ b/src/Requests/Correction/PostCorrections.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Correction; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostCorrections extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/corrections'; + } - public function resolveEndpoint(): string - { - return "/corrections"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Correction/PutCorrection.php b/src/Requests/Correction/PutCorrection.php index 7c9a5a0..eae72b2 100644 --- a/src/Requests/Correction/PutCorrection.php +++ b/src/Requests/Correction/PutCorrection.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Correction; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; use Timatic\SDK\Foundation\Model; @@ -12,29 +11,22 @@ */ class PutCorrection extends Request { - protected Method $method = Method::PUT; - - - public function resolveEndpoint(): string - { - return "/corrections/{$this->correction}"; - } - - - /** - * @param string $correction - */ - public function __construct( - protected string $correction, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected Method $method = Method::PUT; + + public function resolveEndpoint(): string + { + return "/corrections/{$this->correction}"; + } + + public function __construct( + protected string $correction, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Customer/DeleteCustomer.php b/src/Requests/Customer/DeleteCustomer.php index d243be6..a3c7684 100644 --- a/src/Requests/Customer/DeleteCustomer.php +++ b/src/Requests/Customer/DeleteCustomer.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Customer; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * deleteCustomer */ class DeleteCustomer extends Request { - protected Method $method = Method::DELETE; + protected Method $method = Method::DELETE; + public function resolveEndpoint(): string + { + return "/customers/{$this->customer}"; + } - public function resolveEndpoint(): string - { - return "/customers/{$this->customer}"; - } - - - /** - * @param string $customer - */ - public function __construct( - protected string $customer, - ) { - } + public function __construct( + protected string $customer, + ) {} } diff --git a/src/Requests/Customer/GetCustomer.php b/src/Requests/Customer/GetCustomer.php index 4b1d119..efad783 100644 --- a/src/Requests/Customer/GetCustomer.php +++ b/src/Requests/Customer/GetCustomer.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Customer; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getCustomer */ class GetCustomer extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/customers/{$this->customer}"; + } - public function resolveEndpoint(): string - { - return "/customers/{$this->customer}"; - } - - - /** - * @param string $customer - */ - public function __construct( - protected string $customer, - ) { - } + public function __construct( + protected string $customer, + ) {} } diff --git a/src/Requests/Customer/PatchCustomer.php b/src/Requests/Customer/PatchCustomer.php index 0ac17f0..aef2076 100644 --- a/src/Requests/Customer/PatchCustomer.php +++ b/src/Requests/Customer/PatchCustomer.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Customer; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PatchCustomer extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::PATCH; - - - public function resolveEndpoint(): string - { - return "/customers/{$this->customer}"; - } - - - /** - * @param string $customer - */ - public function __construct( - protected string $customer, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::PATCH; + + public function resolveEndpoint(): string + { + return "/customers/{$this->customer}"; + } + + public function __construct( + protected string $customer, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Customer/PostCustomers.php b/src/Requests/Customer/PostCustomers.php index b90a80e..db51e34 100644 --- a/src/Requests/Customer/PostCustomers.php +++ b/src/Requests/Customer/PostCustomers.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Customer; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostCustomers extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/customers'; + } - public function resolveEndpoint(): string - { - return "/customers"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Customer/PutCustomer.php b/src/Requests/Customer/PutCustomer.php index 746c029..c7672a2 100644 --- a/src/Requests/Customer/PutCustomer.php +++ b/src/Requests/Customer/PutCustomer.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Customer; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; use Timatic\SDK\Foundation\Model; @@ -12,29 +11,22 @@ */ class PutCustomer extends Request { - protected Method $method = Method::PUT; - - - public function resolveEndpoint(): string - { - return "/customers/{$this->customer}"; - } - - - /** - * @param string $customer - */ - public function __construct( - protected string $customer, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected Method $method = Method::PUT; + + public function resolveEndpoint(): string + { + return "/customers/{$this->customer}"; + } + + public function __construct( + protected string $customer, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/EntriesExport/GetBudgetEntriesExport.php b/src/Requests/EntriesExport/GetBudgetEntriesExport.php index 333c80c..d29eb92 100644 --- a/src/Requests/EntriesExport/GetBudgetEntriesExport.php +++ b/src/Requests/EntriesExport/GetBudgetEntriesExport.php @@ -2,241 +2,164 @@ namespace Timatic\SDK\Requests\EntriesExport; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getBudgetEntriesExport */ class GetBudgetEntriesExport extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/budgets/{$this->budget}/entries-export"; + } - public function resolveEndpoint(): string - { - return "/budgets/{$this->budget}/entries-export"; - } + public function __construct( + protected string $budget, + protected ?string $filteruserId = null, + protected ?string $filteruserIdeq = null, + protected ?string $filteruserIdnq = null, + protected ?string $filteruserIdgt = null, + protected ?string $filteruserIdlt = null, + protected ?string $filteruserIdgte = null, + protected ?string $filteruserIdlte = null, + protected ?string $filteruserIdcontains = null, + protected ?string $filterbudgetId = null, + protected ?string $filterbudgetIdeq = null, + protected ?string $filterbudgetIdnq = null, + protected ?string $filterbudgetIdgt = null, + protected ?string $filterbudgetIdlt = null, + protected ?string $filterbudgetIdgte = null, + protected ?string $filterbudgetIdlte = null, + protected ?string $filterbudgetIdcontains = null, + protected ?string $filterstartedAt = null, + protected ?string $filterstartedAteq = null, + protected ?string $filterstartedAtnq = null, + protected ?string $filterstartedAtgt = null, + protected ?string $filterstartedAtlt = null, + protected ?string $filterstartedAtgte = null, + protected ?string $filterstartedAtlte = null, + protected ?string $filterstartedAtcontains = null, + protected ?string $filterendedAt = null, + protected ?string $filterendedAteq = null, + protected ?string $filterendedAtnq = null, + protected ?string $filterendedAtgt = null, + protected ?string $filterendedAtlt = null, + protected ?string $filterendedAtgte = null, + protected ?string $filterendedAtlte = null, + protected ?string $filterendedAtcontains = null, + protected ?string $filterhasOvertime = null, + protected ?string $filterhasOvertimeeq = null, + protected ?string $filterhasOvertimenq = null, + protected ?string $filterhasOvertimegt = null, + protected ?string $filterhasOvertimelt = null, + protected ?string $filterhasOvertimegte = null, + protected ?string $filterhasOvertimelte = null, + protected ?string $filterhasOvertimecontains = null, + protected ?string $filteruserFullName = null, + protected ?string $filteruserFullNameeq = null, + protected ?string $filteruserFullNamenq = null, + protected ?string $filteruserFullNamegt = null, + protected ?string $filteruserFullNamelt = null, + protected ?string $filteruserFullNamegte = null, + protected ?string $filteruserFullNamelte = null, + protected ?string $filteruserFullNamecontains = null, + protected ?string $filtercustomerId = null, + protected ?string $filtercustomerIdeq = null, + protected ?string $filtercustomerIdnq = null, + protected ?string $filtercustomerIdgt = null, + protected ?string $filtercustomerIdlt = null, + protected ?string $filtercustomerIdgte = null, + protected ?string $filtercustomerIdlte = null, + protected ?string $filtercustomerIdcontains = null, + protected ?string $filterticketNumber = null, + protected ?string $filterticketNumbereq = null, + protected ?string $filterticketNumbernq = null, + protected ?string $filterticketNumbergt = null, + protected ?string $filterticketNumberlt = null, + protected ?string $filterticketNumbergte = null, + protected ?string $filterticketNumberlte = null, + protected ?string $filterticketNumbercontains = null, + protected ?string $filtersettlement = null, + protected ?string $filterisInvoiced = null, + protected ?string $filterisInvoiceable = null, + protected ?string $include = null, + ) {} - - /** - * @param string $budget - * @param null|string $filteruserId - * @param null|string $filteruserIdeq - * @param null|string $filteruserIdnq - * @param null|string $filteruserIdgt - * @param null|string $filteruserIdlt - * @param null|string $filteruserIdgte - * @param null|string $filteruserIdlte - * @param null|string $filteruserIdcontains - * @param null|string $filterbudgetId - * @param null|string $filterbudgetIdeq - * @param null|string $filterbudgetIdnq - * @param null|string $filterbudgetIdgt - * @param null|string $filterbudgetIdlt - * @param null|string $filterbudgetIdgte - * @param null|string $filterbudgetIdlte - * @param null|string $filterbudgetIdcontains - * @param null|string $filterstartedAt - * @param null|string $filterstartedAteq - * @param null|string $filterstartedAtnq - * @param null|string $filterstartedAtgt - * @param null|string $filterstartedAtlt - * @param null|string $filterstartedAtgte - * @param null|string $filterstartedAtlte - * @param null|string $filterstartedAtcontains - * @param null|string $filterendedAt - * @param null|string $filterendedAteq - * @param null|string $filterendedAtnq - * @param null|string $filterendedAtgt - * @param null|string $filterendedAtlt - * @param null|string $filterendedAtgte - * @param null|string $filterendedAtlte - * @param null|string $filterendedAtcontains - * @param null|string $filterhasOvertime - * @param null|string $filterhasOvertimeeq - * @param null|string $filterhasOvertimenq - * @param null|string $filterhasOvertimegt - * @param null|string $filterhasOvertimelt - * @param null|string $filterhasOvertimegte - * @param null|string $filterhasOvertimelte - * @param null|string $filterhasOvertimecontains - * @param null|string $filteruserFullName - * @param null|string $filteruserFullNameeq - * @param null|string $filteruserFullNamenq - * @param null|string $filteruserFullNamegt - * @param null|string $filteruserFullNamelt - * @param null|string $filteruserFullNamegte - * @param null|string $filteruserFullNamelte - * @param null|string $filteruserFullNamecontains - * @param null|string $filtercustomerId - * @param null|string $filtercustomerIdeq - * @param null|string $filtercustomerIdnq - * @param null|string $filtercustomerIdgt - * @param null|string $filtercustomerIdlt - * @param null|string $filtercustomerIdgte - * @param null|string $filtercustomerIdlte - * @param null|string $filtercustomerIdcontains - * @param null|string $filterticketNumber - * @param null|string $filterticketNumbereq - * @param null|string $filterticketNumbernq - * @param null|string $filterticketNumbergt - * @param null|string $filterticketNumberlt - * @param null|string $filterticketNumbergte - * @param null|string $filterticketNumberlte - * @param null|string $filterticketNumbercontains - * @param null|string $filtersettlement - * @param null|string $filterisInvoiced - * @param null|string $filterisInvoiceable - * @param null|string $include - */ - public function __construct( - protected string $budget, - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - protected ?string $filteruserIdnq = null, - protected ?string $filteruserIdgt = null, - protected ?string $filteruserIdlt = null, - protected ?string $filteruserIdgte = null, - protected ?string $filteruserIdlte = null, - protected ?string $filteruserIdcontains = null, - protected ?string $filterbudgetId = null, - protected ?string $filterbudgetIdeq = null, - protected ?string $filterbudgetIdnq = null, - protected ?string $filterbudgetIdgt = null, - protected ?string $filterbudgetIdlt = null, - protected ?string $filterbudgetIdgte = null, - protected ?string $filterbudgetIdlte = null, - protected ?string $filterbudgetIdcontains = null, - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterhasOvertime = null, - protected ?string $filterhasOvertimeeq = null, - protected ?string $filterhasOvertimenq = null, - protected ?string $filterhasOvertimegt = null, - protected ?string $filterhasOvertimelt = null, - protected ?string $filterhasOvertimegte = null, - protected ?string $filterhasOvertimelte = null, - protected ?string $filterhasOvertimecontains = null, - protected ?string $filteruserFullName = null, - protected ?string $filteruserFullNameeq = null, - protected ?string $filteruserFullNamenq = null, - protected ?string $filteruserFullNamegt = null, - protected ?string $filteruserFullNamelt = null, - protected ?string $filteruserFullNamegte = null, - protected ?string $filteruserFullNamelte = null, - protected ?string $filteruserFullNamecontains = null, - protected ?string $filtercustomerId = null, - protected ?string $filtercustomerIdeq = null, - protected ?string $filtercustomerIdnq = null, - protected ?string $filtercustomerIdgt = null, - protected ?string $filtercustomerIdlt = null, - protected ?string $filtercustomerIdgte = null, - protected ?string $filtercustomerIdlte = null, - protected ?string $filtercustomerIdcontains = null, - protected ?string $filterticketNumber = null, - protected ?string $filterticketNumbereq = null, - protected ?string $filterticketNumbernq = null, - protected ?string $filterticketNumbergt = null, - protected ?string $filterticketNumberlt = null, - protected ?string $filterticketNumbergte = null, - protected ?string $filterticketNumberlte = null, - protected ?string $filterticketNumbercontains = null, - protected ?string $filtersettlement = null, - protected ?string $filterisInvoiced = null, - protected ?string $filterisInvoiceable = null, - protected ?string $include = null, - ) { - } - - - public function defaultQuery(): array - { - return array_filter([ - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - 'filter[userId][nq]' => $this->filteruserIdnq, - 'filter[userId][gt]' => $this->filteruserIdgt, - 'filter[userId][lt]' => $this->filteruserIdlt, - 'filter[userId][gte]' => $this->filteruserIdgte, - 'filter[userId][lte]' => $this->filteruserIdlte, - 'filter[userId][contains]' => $this->filteruserIdcontains, - 'filter[budgetId]' => $this->filterbudgetId, - 'filter[budgetId][eq]' => $this->filterbudgetIdeq, - 'filter[budgetId][nq]' => $this->filterbudgetIdnq, - 'filter[budgetId][gt]' => $this->filterbudgetIdgt, - 'filter[budgetId][lt]' => $this->filterbudgetIdlt, - 'filter[budgetId][gte]' => $this->filterbudgetIdgte, - 'filter[budgetId][lte]' => $this->filterbudgetIdlte, - 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[hasOvertime]' => $this->filterhasOvertime, - 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, - 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, - 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, - 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, - 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, - 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, - 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, - 'filter[userFullName]' => $this->filteruserFullName, - 'filter[userFullName][eq]' => $this->filteruserFullNameeq, - 'filter[userFullName][nq]' => $this->filteruserFullNamenq, - 'filter[userFullName][gt]' => $this->filteruserFullNamegt, - 'filter[userFullName][lt]' => $this->filteruserFullNamelt, - 'filter[userFullName][gte]' => $this->filteruserFullNamegte, - 'filter[userFullName][lte]' => $this->filteruserFullNamelte, - 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, - 'filter[customerId]' => $this->filtercustomerId, - 'filter[customerId][eq]' => $this->filtercustomerIdeq, - 'filter[customerId][nq]' => $this->filtercustomerIdnq, - 'filter[customerId][gt]' => $this->filtercustomerIdgt, - 'filter[customerId][lt]' => $this->filtercustomerIdlt, - 'filter[customerId][gte]' => $this->filtercustomerIdgte, - 'filter[customerId][lte]' => $this->filtercustomerIdlte, - 'filter[customerId][contains]' => $this->filtercustomerIdcontains, - 'filter[ticketNumber]' => $this->filterticketNumber, - 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, - 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, - 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, - 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, - 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, - 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, - 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, - 'filter[settlement]' => $this->filtersettlement, - 'filter[isInvoiced]' => $this->filterisInvoiced, - 'filter[isInvoiceable]' => $this->filterisInvoiceable, - 'include' => $this->include, - ]); - } + public function defaultQuery(): array + { + return array_filter([ + 'filter[userId]' => $this->filteruserId, + 'filter[userId][eq]' => $this->filteruserIdeq, + 'filter[userId][nq]' => $this->filteruserIdnq, + 'filter[userId][gt]' => $this->filteruserIdgt, + 'filter[userId][lt]' => $this->filteruserIdlt, + 'filter[userId][gte]' => $this->filteruserIdgte, + 'filter[userId][lte]' => $this->filteruserIdlte, + 'filter[userId][contains]' => $this->filteruserIdcontains, + 'filter[budgetId]' => $this->filterbudgetId, + 'filter[budgetId][eq]' => $this->filterbudgetIdeq, + 'filter[budgetId][nq]' => $this->filterbudgetIdnq, + 'filter[budgetId][gt]' => $this->filterbudgetIdgt, + 'filter[budgetId][lt]' => $this->filterbudgetIdlt, + 'filter[budgetId][gte]' => $this->filterbudgetIdgte, + 'filter[budgetId][lte]' => $this->filterbudgetIdlte, + 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, + 'filter[startedAt]' => $this->filterstartedAt, + 'filter[startedAt][eq]' => $this->filterstartedAteq, + 'filter[startedAt][nq]' => $this->filterstartedAtnq, + 'filter[startedAt][gt]' => $this->filterstartedAtgt, + 'filter[startedAt][lt]' => $this->filterstartedAtlt, + 'filter[startedAt][gte]' => $this->filterstartedAtgte, + 'filter[startedAt][lte]' => $this->filterstartedAtlte, + 'filter[startedAt][contains]' => $this->filterstartedAtcontains, + 'filter[endedAt]' => $this->filterendedAt, + 'filter[endedAt][eq]' => $this->filterendedAteq, + 'filter[endedAt][nq]' => $this->filterendedAtnq, + 'filter[endedAt][gt]' => $this->filterendedAtgt, + 'filter[endedAt][lt]' => $this->filterendedAtlt, + 'filter[endedAt][gte]' => $this->filterendedAtgte, + 'filter[endedAt][lte]' => $this->filterendedAtlte, + 'filter[endedAt][contains]' => $this->filterendedAtcontains, + 'filter[hasOvertime]' => $this->filterhasOvertime, + 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, + 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, + 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, + 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, + 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, + 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, + 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, + 'filter[userFullName]' => $this->filteruserFullName, + 'filter[userFullName][eq]' => $this->filteruserFullNameeq, + 'filter[userFullName][nq]' => $this->filteruserFullNamenq, + 'filter[userFullName][gt]' => $this->filteruserFullNamegt, + 'filter[userFullName][lt]' => $this->filteruserFullNamelt, + 'filter[userFullName][gte]' => $this->filteruserFullNamegte, + 'filter[userFullName][lte]' => $this->filteruserFullNamelte, + 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, + 'filter[customerId]' => $this->filtercustomerId, + 'filter[customerId][eq]' => $this->filtercustomerIdeq, + 'filter[customerId][nq]' => $this->filtercustomerIdnq, + 'filter[customerId][gt]' => $this->filtercustomerIdgt, + 'filter[customerId][lt]' => $this->filtercustomerIdlt, + 'filter[customerId][gte]' => $this->filtercustomerIdgte, + 'filter[customerId][lte]' => $this->filtercustomerIdlte, + 'filter[customerId][contains]' => $this->filtercustomerIdcontains, + 'filter[ticketNumber]' => $this->filterticketNumber, + 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, + 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, + 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, + 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, + 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, + 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, + 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, + 'filter[settlement]' => $this->filtersettlement, + 'filter[isInvoiced]' => $this->filterisInvoiced, + 'filter[isInvoiceable]' => $this->filterisInvoiceable, + 'include' => $this->include, + ]); + } } diff --git a/src/Requests/Entry/DeleteEntry.php b/src/Requests/Entry/DeleteEntry.php index d959f21..9d986a2 100644 --- a/src/Requests/Entry/DeleteEntry.php +++ b/src/Requests/Entry/DeleteEntry.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Entry; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * deleteEntry */ class DeleteEntry extends Request { - protected Method $method = Method::DELETE; + protected Method $method = Method::DELETE; + public function resolveEndpoint(): string + { + return "/entries/{$this->entry}"; + } - public function resolveEndpoint(): string - { - return "/entries/{$this->entry}"; - } - - - /** - * @param string $entry - */ - public function __construct( - protected string $entry, - ) { - } + public function __construct( + protected string $entry, + ) {} } diff --git a/src/Requests/Entry/GetEntry.php b/src/Requests/Entry/GetEntry.php index 83e487d..f8aac90 100644 --- a/src/Requests/Entry/GetEntry.php +++ b/src/Requests/Entry/GetEntry.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Entry; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getEntry */ class GetEntry extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/entries/{$this->entry}"; + } - public function resolveEndpoint(): string - { - return "/entries/{$this->entry}"; - } - - - /** - * @param string $entry - */ - public function __construct( - protected string $entry, - ) { - } + public function __construct( + protected string $entry, + ) {} } diff --git a/src/Requests/Entry/PatchEntry.php b/src/Requests/Entry/PatchEntry.php index bd14f2f..80a18df 100644 --- a/src/Requests/Entry/PatchEntry.php +++ b/src/Requests/Entry/PatchEntry.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Entry; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PatchEntry extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::PATCH; - - - public function resolveEndpoint(): string - { - return "/entries/{$this->entry}"; - } - - - /** - * @param string $entry - */ - public function __construct( - protected string $entry, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::PATCH; + + public function resolveEndpoint(): string + { + return "/entries/{$this->entry}"; + } + + public function __construct( + protected string $entry, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Entry/PostEntries.php b/src/Requests/Entry/PostEntries.php index 650541b..79f2d26 100644 --- a/src/Requests/Entry/PostEntries.php +++ b/src/Requests/Entry/PostEntries.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Entry; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostEntries extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/entries'; + } - public function resolveEndpoint(): string - { - return "/entries"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Entry/PutEntry.php b/src/Requests/Entry/PutEntry.php index 06e675b..0c4e467 100644 --- a/src/Requests/Entry/PutEntry.php +++ b/src/Requests/Entry/PutEntry.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Entry; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; use Timatic\SDK\Foundation\Model; @@ -12,29 +11,22 @@ */ class PutEntry extends Request { - protected Method $method = Method::PUT; - - - public function resolveEndpoint(): string - { - return "/entries/{$this->entry}"; - } - - - /** - * @param string $entry - */ - public function __construct( - protected string $entry, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected Method $method = Method::PUT; + + public function resolveEndpoint(): string + { + return "/entries/{$this->entry}"; + } + + public function __construct( + protected string $entry, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php b/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php index 5a4d3dc..c0b2bf2 100644 --- a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php +++ b/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\EntrySuggestion; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * deleteEntrySuggestion */ class DeleteEntrySuggestion extends Request { - protected Method $method = Method::DELETE; + protected Method $method = Method::DELETE; + public function resolveEndpoint(): string + { + return "/entry-suggestions/{$this->entrySuggestion}"; + } - public function resolveEndpoint(): string - { - return "/entry-suggestions/{$this->entrySuggestion}"; - } - - - /** - * @param string $entrySuggestion - */ - public function __construct( - protected string $entrySuggestion, - ) { - } + public function __construct( + protected string $entrySuggestion, + ) {} } diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestion.php b/src/Requests/EntrySuggestion/GetEntrySuggestion.php index db4251a..731de98 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestion.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestion.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\EntrySuggestion; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getEntrySuggestion */ class GetEntrySuggestion extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/entry-suggestions/{$this->entrySuggestion}"; + } - public function resolveEndpoint(): string - { - return "/entry-suggestions/{$this->entrySuggestion}"; - } - - - /** - * @param string $entrySuggestion - */ - public function __construct( - protected string $entrySuggestion, - ) { - } + public function __construct( + protected string $entrySuggestion, + ) {} } diff --git a/src/Requests/Event/PostEvents.php b/src/Requests/Event/PostEvents.php index a9ef803..5db188b 100644 --- a/src/Requests/Event/PostEvents.php +++ b/src/Requests/Event/PostEvents.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Event; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostEvents extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/events'; + } - public function resolveEndpoint(): string - { - return "/events"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Incident/GetIncident.php b/src/Requests/Incident/GetIncident.php index d71886b..ccb5999 100644 --- a/src/Requests/Incident/GetIncident.php +++ b/src/Requests/Incident/GetIncident.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Incident; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getIncident */ class GetIncident extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/incidents/{$this->incident}"; + } - public function resolveEndpoint(): string - { - return "/incidents/{$this->incident}"; - } - - - /** - * @param string $incident - */ - public function __construct( - protected string $incident, - ) { - } + public function __construct( + protected string $incident, + ) {} } diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php index fa89b17..224b6ba 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\MarkAsExported; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PostOvertimeMarkAsExported extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::POST; - - - public function resolveEndpoint(): string - { - return "/overtimes/{$this->overtime}/mark-as-exported"; - } - - - /** - * @param string $overtime - */ - public function __construct( - protected string $overtime, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::POST; + + public function resolveEndpoint(): string + { + return "/overtimes/{$this->overtime}/mark-as-exported"; + } + + public function __construct( + protected string $overtime, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php index a2d8530..f723ff6 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\MarkAsInvoiced; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PostEntryMarkAsInvoiced extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::POST; - - - public function resolveEndpoint(): string - { - return "/entries/{$this->entry}/mark-as-invoiced"; - } - - - /** - * @param string $entry - */ - public function __construct( - protected string $entry, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::POST; + + public function resolveEndpoint(): string + { + return "/entries/{$this->entry}/mark-as-invoiced"; + } + + public function __construct( + protected string $entry, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Number/GetIncidentsNumber.php b/src/Requests/Number/GetIncidentsNumber.php index 503495c..1c2af6d 100644 --- a/src/Requests/Number/GetIncidentsNumber.php +++ b/src/Requests/Number/GetIncidentsNumber.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Number; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getIncidentsNumber */ class GetIncidentsNumber extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/incidents/number/{$this->incident}"; + } - public function resolveEndpoint(): string - { - return "/incidents/number/{$this->incident}"; - } - - - /** - * @param string $incident - */ - public function __construct( - protected string $incident, - ) { - } + public function __construct( + protected string $incident, + ) {} } diff --git a/src/Requests/Period/GetBudgetPeriods.php b/src/Requests/Period/GetBudgetPeriods.php index 638d6a7..575a984 100644 --- a/src/Requests/Period/GetBudgetPeriods.php +++ b/src/Requests/Period/GetBudgetPeriods.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Period; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getBudgetPeriods */ class GetBudgetPeriods extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/budgets/{$this->budget}/periods"; + } - public function resolveEndpoint(): string - { - return "/budgets/{$this->budget}/periods"; - } - - - /** - * @param string $budget - */ - public function __construct( - protected string $budget, - ) { - } + public function __construct( + protected string $budget, + ) {} } diff --git a/src/Requests/Team/DeleteTeam.php b/src/Requests/Team/DeleteTeam.php index 7db85c6..20f2201 100644 --- a/src/Requests/Team/DeleteTeam.php +++ b/src/Requests/Team/DeleteTeam.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Team; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * deleteTeam */ class DeleteTeam extends Request { - protected Method $method = Method::DELETE; + protected Method $method = Method::DELETE; + public function resolveEndpoint(): string + { + return "/teams/{$this->team}"; + } - public function resolveEndpoint(): string - { - return "/teams/{$this->team}"; - } - - - /** - * @param string $team - */ - public function __construct( - protected string $team, - ) { - } + public function __construct( + protected string $team, + ) {} } diff --git a/src/Requests/Team/GetTeam.php b/src/Requests/Team/GetTeam.php index 012d0d1..8d83ef1 100644 --- a/src/Requests/Team/GetTeam.php +++ b/src/Requests/Team/GetTeam.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\Team; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getTeam */ class GetTeam extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/teams/{$this->team}"; + } - public function resolveEndpoint(): string - { - return "/teams/{$this->team}"; - } - - - /** - * @param string $team - */ - public function __construct( - protected string $team, - ) { - } + public function __construct( + protected string $team, + ) {} } diff --git a/src/Requests/Team/PatchTeam.php b/src/Requests/Team/PatchTeam.php index 9607890..5576543 100644 --- a/src/Requests/Team/PatchTeam.php +++ b/src/Requests/Team/PatchTeam.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Team; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PatchTeam extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::PATCH; - - - public function resolveEndpoint(): string - { - return "/teams/{$this->team}"; - } - - - /** - * @param string $team - */ - public function __construct( - protected string $team, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::PATCH; + + public function resolveEndpoint(): string + { + return "/teams/{$this->team}"; + } + + public function __construct( + protected string $team, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Team/PostTeams.php b/src/Requests/Team/PostTeams.php index 63900cf..9c511c4 100644 --- a/src/Requests/Team/PostTeams.php +++ b/src/Requests/Team/PostTeams.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Team; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostTeams extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/teams'; + } - public function resolveEndpoint(): string - { - return "/teams"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/Team/PutTeam.php b/src/Requests/Team/PutTeam.php index be53927..e581007 100644 --- a/src/Requests/Team/PutTeam.php +++ b/src/Requests/Team/PutTeam.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\Team; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; use Timatic\SDK\Foundation\Model; @@ -12,29 +11,22 @@ */ class PutTeam extends Request { - protected Method $method = Method::PUT; - - - public function resolveEndpoint(): string - { - return "/teams/{$this->team}"; - } - - - /** - * @param string $team - */ - public function __construct( - protected string $team, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected Method $method = Method::PUT; + + public function resolveEndpoint(): string + { + return "/teams/{$this->team}"; + } + + public function __construct( + protected string $team, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/User/DeleteUser.php b/src/Requests/User/DeleteUser.php index 4a253ab..f0fc71a 100644 --- a/src/Requests/User/DeleteUser.php +++ b/src/Requests/User/DeleteUser.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\User; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * deleteUser */ class DeleteUser extends Request { - protected Method $method = Method::DELETE; + protected Method $method = Method::DELETE; + public function resolveEndpoint(): string + { + return "/users/{$this->user}"; + } - public function resolveEndpoint(): string - { - return "/users/{$this->user}"; - } - - - /** - * @param string $user - */ - public function __construct( - protected string $user, - ) { - } + public function __construct( + protected string $user, + ) {} } diff --git a/src/Requests/User/GetUser.php b/src/Requests/User/GetUser.php index 32df144..ebcbe5d 100644 --- a/src/Requests/User/GetUser.php +++ b/src/Requests/User/GetUser.php @@ -2,30 +2,22 @@ namespace Timatic\SDK\Requests\User; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; -use Timatic\SDK\Foundation\Model; /** * getUser */ class GetUser extends Request { - protected Method $method = Method::GET; + protected Method $method = Method::GET; + public function resolveEndpoint(): string + { + return "/users/{$this->user}"; + } - public function resolveEndpoint(): string - { - return "/users/{$this->user}"; - } - - - /** - * @param string $user - */ - public function __construct( - protected string $user, - ) { - } + public function __construct( + protected string $user, + ) {} } diff --git a/src/Requests/User/PatchUser.php b/src/Requests/User/PatchUser.php index bc460d5..f7cfcbe 100644 --- a/src/Requests/User/PatchUser.php +++ b/src/Requests/User/PatchUser.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\User; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,31 +13,24 @@ */ class PatchUser extends Request implements HasBody { - use HasJsonBody; - - protected Method $method = Method::PATCH; - - - public function resolveEndpoint(): string - { - return "/users/{$this->user}"; - } - - - /** - * @param string $user - */ - public function __construct( - protected string $user, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + use HasJsonBody; + + protected Method $method = Method::PATCH; + + public function resolveEndpoint(): string + { + return "/users/{$this->user}"; + } + + public function __construct( + protected string $user, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/User/PostUsers.php b/src/Requests/User/PostUsers.php index 6749bbf..92dc07e 100644 --- a/src/Requests/User/PostUsers.php +++ b/src/Requests/User/PostUsers.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\User; -use DateTime; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; @@ -14,27 +13,23 @@ */ class PostUsers extends Request implements HasBody { - use HasJsonBody; + use HasJsonBody; - protected Method $method = Method::POST; + protected Method $method = Method::POST; + public function resolveEndpoint(): string + { + return '/users'; + } - public function resolveEndpoint(): string - { - return "/users"; - } + public function __construct( + protected Model|array $data, + ) {} - - public function __construct( - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Requests/User/PutUser.php b/src/Requests/User/PutUser.php index bab4df7..4133fe7 100644 --- a/src/Requests/User/PutUser.php +++ b/src/Requests/User/PutUser.php @@ -2,7 +2,6 @@ namespace Timatic\SDK\Requests\User; -use DateTime; use Saloon\Enums\Method; use Saloon\Http\Request; use Timatic\SDK\Foundation\Model; @@ -12,29 +11,22 @@ */ class PutUser extends Request { - protected Method $method = Method::PUT; - - - public function resolveEndpoint(): string - { - return "/users/{$this->user}"; - } - - - /** - * @param string $user - */ - public function __construct( - protected string $user, - protected Model|array $data, - ) { - } - - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } + protected Method $method = Method::PUT; + + public function resolveEndpoint(): string + { + return "/users/{$this->user}"; + } + + public function __construct( + protected string $user, + protected Model|array $data, + ) {} + + protected function defaultBody(): array + { + return $this->data instanceof Model + ? $this->data->toJsonApi() + : ['data' => $this->data]; + } } diff --git a/src/Resource/BudgetTimeSpentTotal.php b/src/Resource/BudgetTimeSpentTotal.php index 83b50b5..f920c8d 100644 --- a/src/Resource/BudgetTimeSpentTotal.php +++ b/src/Resource/BudgetTimeSpentTotal.php @@ -8,12 +8,8 @@ class BudgetTimeSpentTotal extends BaseResource { - /** - * @param string $filterbudgetId - * @param string $filterbudgetIdeq - */ - public function getBudgetTimeSpentTotals(?string $filterbudgetId = null, ?string $filterbudgetIdeq = null): Response - { - return $this->connector->send(new GetBudgetTimeSpentTotals($filterbudgetId, $filterbudgetIdeq)); - } + public function getBudgetTimeSpentTotals(?string $filterbudgetId = null, ?string $filterbudgetIdeq = null): Response + { + return $this->connector->send(new GetBudgetTimeSpentTotals($filterbudgetId, $filterbudgetIdeq)); + } } diff --git a/src/Resource/BudgetType.php b/src/Resource/BudgetType.php index 10a0141..9727ada 100644 --- a/src/Resource/BudgetType.php +++ b/src/Resource/BudgetType.php @@ -8,8 +8,8 @@ class BudgetType extends BaseResource { - public function getBudgetTypes(): Response - { - return $this->connector->send(new GetBudgetTypes()); - } + public function getBudgetTypes(): Response + { + return $this->connector->send(new GetBudgetTypes); + } } diff --git a/src/Resource/Change.php b/src/Resource/Change.php index bf245c3..fffd7ff 100644 --- a/src/Resource/Change.php +++ b/src/Resource/Change.php @@ -9,17 +9,13 @@ class Change extends BaseResource { - /** - * @param string $change - */ - public function getChange(string $change): Response - { - return $this->connector->send(new GetChange($change)); - } + public function getChange(string $change): Response + { + return $this->connector->send(new GetChange($change)); + } - - public function getChanges(): Response - { - return $this->connector->send(new GetChanges()); - } + public function getChanges(): Response + { + return $this->connector->send(new GetChanges); + } } diff --git a/src/Resource/DailyProgress.php b/src/Resource/DailyProgress.php index f5d2805..a125ff8 100644 --- a/src/Resource/DailyProgress.php +++ b/src/Resource/DailyProgress.php @@ -8,8 +8,8 @@ class DailyProgress extends BaseResource { - public function getDailyProgresses(): Response - { - return $this->connector->send(new GetDailyProgresses()); - } + public function getDailyProgresses(): Response + { + return $this->connector->send(new GetDailyProgresses); + } } diff --git a/src/Resource/EntriesExport.php b/src/Resource/EntriesExport.php index bbbc72f..12a1cb6 100644 --- a/src/Resource/EntriesExport.php +++ b/src/Resource/EntriesExport.php @@ -8,149 +8,77 @@ class EntriesExport extends BaseResource { - /** - * @param string $budget - * @param string $filteruserId - * @param string $filteruserIdeq - * @param string $filteruserIdnq - * @param string $filteruserIdgt - * @param string $filteruserIdlt - * @param string $filteruserIdgte - * @param string $filteruserIdlte - * @param string $filteruserIdcontains - * @param string $filterbudgetId - * @param string $filterbudgetIdeq - * @param string $filterbudgetIdnq - * @param string $filterbudgetIdgt - * @param string $filterbudgetIdlt - * @param string $filterbudgetIdgte - * @param string $filterbudgetIdlte - * @param string $filterbudgetIdcontains - * @param string $filterstartedAt - * @param string $filterstartedAteq - * @param string $filterstartedAtnq - * @param string $filterstartedAtgt - * @param string $filterstartedAtlt - * @param string $filterstartedAtgte - * @param string $filterstartedAtlte - * @param string $filterstartedAtcontains - * @param string $filterendedAt - * @param string $filterendedAteq - * @param string $filterendedAtnq - * @param string $filterendedAtgt - * @param string $filterendedAtlt - * @param string $filterendedAtgte - * @param string $filterendedAtlte - * @param string $filterendedAtcontains - * @param string $filterhasOvertime - * @param string $filterhasOvertimeeq - * @param string $filterhasOvertimenq - * @param string $filterhasOvertimegt - * @param string $filterhasOvertimelt - * @param string $filterhasOvertimegte - * @param string $filterhasOvertimelte - * @param string $filterhasOvertimecontains - * @param string $filteruserFullName - * @param string $filteruserFullNameeq - * @param string $filteruserFullNamenq - * @param string $filteruserFullNamegt - * @param string $filteruserFullNamelt - * @param string $filteruserFullNamegte - * @param string $filteruserFullNamelte - * @param string $filteruserFullNamecontains - * @param string $filtercustomerId - * @param string $filtercustomerIdeq - * @param string $filtercustomerIdnq - * @param string $filtercustomerIdgt - * @param string $filtercustomerIdlt - * @param string $filtercustomerIdgte - * @param string $filtercustomerIdlte - * @param string $filtercustomerIdcontains - * @param string $filterticketNumber - * @param string $filterticketNumbereq - * @param string $filterticketNumbernq - * @param string $filterticketNumbergt - * @param string $filterticketNumberlt - * @param string $filterticketNumbergte - * @param string $filterticketNumberlte - * @param string $filterticketNumbercontains - * @param string $filtersettlement - * @param string $filterisInvoiced - * @param string $filterisInvoiceable - * @param string $include - */ - public function getBudgetEntriesExport( - string $budget, - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ?string $filteruserIdnq = null, - ?string $filteruserIdgt = null, - ?string $filteruserIdlt = null, - ?string $filteruserIdgte = null, - ?string $filteruserIdlte = null, - ?string $filteruserIdcontains = null, - ?string $filterbudgetId = null, - ?string $filterbudgetIdeq = null, - ?string $filterbudgetIdnq = null, - ?string $filterbudgetIdgt = null, - ?string $filterbudgetIdlt = null, - ?string $filterbudgetIdgte = null, - ?string $filterbudgetIdlte = null, - ?string $filterbudgetIdcontains = null, - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterhasOvertime = null, - ?string $filterhasOvertimeeq = null, - ?string $filterhasOvertimenq = null, - ?string $filterhasOvertimegt = null, - ?string $filterhasOvertimelt = null, - ?string $filterhasOvertimegte = null, - ?string $filterhasOvertimelte = null, - ?string $filterhasOvertimecontains = null, - ?string $filteruserFullName = null, - ?string $filteruserFullNameeq = null, - ?string $filteruserFullNamenq = null, - ?string $filteruserFullNamegt = null, - ?string $filteruserFullNamelt = null, - ?string $filteruserFullNamegte = null, - ?string $filteruserFullNamelte = null, - ?string $filteruserFullNamecontains = null, - ?string $filtercustomerId = null, - ?string $filtercustomerIdeq = null, - ?string $filtercustomerIdnq = null, - ?string $filtercustomerIdgt = null, - ?string $filtercustomerIdlt = null, - ?string $filtercustomerIdgte = null, - ?string $filtercustomerIdlte = null, - ?string $filtercustomerIdcontains = null, - ?string $filterticketNumber = null, - ?string $filterticketNumbereq = null, - ?string $filterticketNumbernq = null, - ?string $filterticketNumbergt = null, - ?string $filterticketNumberlt = null, - ?string $filterticketNumbergte = null, - ?string $filterticketNumberlte = null, - ?string $filterticketNumbercontains = null, - ?string $filtersettlement = null, - ?string $filterisInvoiced = null, - ?string $filterisInvoiceable = null, - ?string $include = null, - ): Response - { - return $this->connector->send(new GetBudgetEntriesExport($budget, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); - } + public function getBudgetEntriesExport( + string $budget, + ?string $filteruserId = null, + ?string $filteruserIdeq = null, + ?string $filteruserIdnq = null, + ?string $filteruserIdgt = null, + ?string $filteruserIdlt = null, + ?string $filteruserIdgte = null, + ?string $filteruserIdlte = null, + ?string $filteruserIdcontains = null, + ?string $filterbudgetId = null, + ?string $filterbudgetIdeq = null, + ?string $filterbudgetIdnq = null, + ?string $filterbudgetIdgt = null, + ?string $filterbudgetIdlt = null, + ?string $filterbudgetIdgte = null, + ?string $filterbudgetIdlte = null, + ?string $filterbudgetIdcontains = null, + ?string $filterstartedAt = null, + ?string $filterstartedAteq = null, + ?string $filterstartedAtnq = null, + ?string $filterstartedAtgt = null, + ?string $filterstartedAtlt = null, + ?string $filterstartedAtgte = null, + ?string $filterstartedAtlte = null, + ?string $filterstartedAtcontains = null, + ?string $filterendedAt = null, + ?string $filterendedAteq = null, + ?string $filterendedAtnq = null, + ?string $filterendedAtgt = null, + ?string $filterendedAtlt = null, + ?string $filterendedAtgte = null, + ?string $filterendedAtlte = null, + ?string $filterendedAtcontains = null, + ?string $filterhasOvertime = null, + ?string $filterhasOvertimeeq = null, + ?string $filterhasOvertimenq = null, + ?string $filterhasOvertimegt = null, + ?string $filterhasOvertimelt = null, + ?string $filterhasOvertimegte = null, + ?string $filterhasOvertimelte = null, + ?string $filterhasOvertimecontains = null, + ?string $filteruserFullName = null, + ?string $filteruserFullNameeq = null, + ?string $filteruserFullNamenq = null, + ?string $filteruserFullNamegt = null, + ?string $filteruserFullNamelt = null, + ?string $filteruserFullNamegte = null, + ?string $filteruserFullNamelte = null, + ?string $filteruserFullNamecontains = null, + ?string $filtercustomerId = null, + ?string $filtercustomerIdeq = null, + ?string $filtercustomerIdnq = null, + ?string $filtercustomerIdgt = null, + ?string $filtercustomerIdlt = null, + ?string $filtercustomerIdgte = null, + ?string $filtercustomerIdlte = null, + ?string $filtercustomerIdcontains = null, + ?string $filterticketNumber = null, + ?string $filterticketNumbereq = null, + ?string $filterticketNumbernq = null, + ?string $filterticketNumbergt = null, + ?string $filterticketNumberlt = null, + ?string $filterticketNumbergte = null, + ?string $filterticketNumberlte = null, + ?string $filterticketNumbercontains = null, + ?string $filtersettlement = null, + ?string $filterisInvoiced = null, + ?string $filterisInvoiceable = null, + ?string $include = null, + ): Response { + return $this->connector->send(new GetBudgetEntriesExport($budget, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + } } diff --git a/src/Resource/EntrySuggestion.php b/src/Resource/EntrySuggestion.php index 3eda02a..217becd 100644 --- a/src/Resource/EntrySuggestion.php +++ b/src/Resource/EntrySuggestion.php @@ -10,45 +10,26 @@ class EntrySuggestion extends BaseResource { - /** - * @param string $filterdate - * @param string $filterdateeq - * @param string $filterdatenq - * @param string $filterdategt - * @param string $filterdatelt - * @param string $filterdategte - * @param string $filterdatelte - * @param string $filterdatecontains - */ - public function getEntrySuggestions( - ?string $filterdate = null, - ?string $filterdateeq = null, - ?string $filterdatenq = null, - ?string $filterdategt = null, - ?string $filterdatelt = null, - ?string $filterdategte = null, - ?string $filterdatelte = null, - ?string $filterdatecontains = null, - ): Response - { - return $this->connector->send(new GetEntrySuggestions($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); - } + public function getEntrySuggestions( + ?string $filterdate = null, + ?string $filterdateeq = null, + ?string $filterdatenq = null, + ?string $filterdategt = null, + ?string $filterdatelt = null, + ?string $filterdategte = null, + ?string $filterdatelte = null, + ?string $filterdatecontains = null, + ): Response { + return $this->connector->send(new GetEntrySuggestions($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); + } + public function getEntrySuggestion(string $entrySuggestion): Response + { + return $this->connector->send(new GetEntrySuggestion($entrySuggestion)); + } - /** - * @param string $entrySuggestion - */ - public function getEntrySuggestion(string $entrySuggestion): Response - { - return $this->connector->send(new GetEntrySuggestion($entrySuggestion)); - } - - - /** - * @param string $entrySuggestion - */ - public function deleteEntrySuggestion(string $entrySuggestion): Response - { - return $this->connector->send(new DeleteEntrySuggestion($entrySuggestion)); - } + public function deleteEntrySuggestion(string $entrySuggestion): Response + { + return $this->connector->send(new DeleteEntrySuggestion($entrySuggestion)); + } } diff --git a/src/Resource/ExportMail.php b/src/Resource/ExportMail.php index 88d5f6d..f151f0b 100644 --- a/src/Resource/ExportMail.php +++ b/src/Resource/ExportMail.php @@ -8,8 +8,8 @@ class ExportMail extends BaseResource { - public function getBudgetsExportMails(): Response - { - return $this->connector->send(new GetBudgetsExportMails()); - } + public function getBudgetsExportMails(): Response + { + return $this->connector->send(new GetBudgetsExportMails); + } } diff --git a/src/Resource/Incident.php b/src/Resource/Incident.php index 06fa6b5..a342574 100644 --- a/src/Resource/Incident.php +++ b/src/Resource/Incident.php @@ -9,17 +9,13 @@ class Incident extends BaseResource { - /** - * @param string $incident - */ - public function getIncident(string $incident): Response - { - return $this->connector->send(new GetIncident($incident)); - } + public function getIncident(string $incident): Response + { + return $this->connector->send(new GetIncident($incident)); + } - - public function getIncidents(): Response - { - return $this->connector->send(new GetIncidents()); - } + public function getIncidents(): Response + { + return $this->connector->send(new GetIncidents); + } } diff --git a/src/Resource/Me.php b/src/Resource/Me.php index 3c58ca2..b77c7fd 100644 --- a/src/Resource/Me.php +++ b/src/Resource/Me.php @@ -8,8 +8,8 @@ class Me extends BaseResource { - public function getMes(): Response - { - return $this->connector->send(new GetMes()); - } + public function getMes(): Response + { + return $this->connector->send(new GetMes); + } } diff --git a/src/Resource/Number.php b/src/Resource/Number.php index f7435ff..58ff377 100644 --- a/src/Resource/Number.php +++ b/src/Resource/Number.php @@ -8,11 +8,8 @@ class Number extends BaseResource { - /** - * @param string $incident - */ - public function getIncidentsNumber(string $incident): Response - { - return $this->connector->send(new GetIncidentsNumber($incident)); - } + public function getIncidentsNumber(string $incident): Response + { + return $this->connector->send(new GetIncidentsNumber($incident)); + } } diff --git a/src/Resource/Overtime.php b/src/Resource/Overtime.php index 8d417f2..ba9b360 100644 --- a/src/Resource/Overtime.php +++ b/src/Resource/Overtime.php @@ -8,63 +8,34 @@ class Overtime extends BaseResource { - /** - * @param string $filterstartedAt - * @param string $filterstartedAteq - * @param string $filterstartedAtnq - * @param string $filterstartedAtgt - * @param string $filterstartedAtlt - * @param string $filterstartedAtgte - * @param string $filterstartedAtlte - * @param string $filterstartedAtcontains - * @param string $filterendedAt - * @param string $filterendedAteq - * @param string $filterendedAtnq - * @param string $filterendedAtgt - * @param string $filterendedAtlt - * @param string $filterendedAtgte - * @param string $filterendedAtlte - * @param string $filterendedAtcontains - * @param string $filterisApproved - * @param string $filterapprovedAt - * @param string $filterapprovedAteq - * @param string $filterapprovedAtnq - * @param string $filterapprovedAtgt - * @param string $filterapprovedAtlt - * @param string $filterapprovedAtgte - * @param string $filterapprovedAtlte - * @param string $filterapprovedAtcontains - * @param string $filterisExported - */ - public function getOvertimes( - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterisApproved = null, - ?string $filterapprovedAt = null, - ?string $filterapprovedAteq = null, - ?string $filterapprovedAtnq = null, - ?string $filterapprovedAtgt = null, - ?string $filterapprovedAtlt = null, - ?string $filterapprovedAtgte = null, - ?string $filterapprovedAtlte = null, - ?string $filterapprovedAtcontains = null, - ?string $filterisExported = null, - ): Response - { - return $this->connector->send(new GetOvertimes($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); - } + public function getOvertimes( + ?string $filterstartedAt = null, + ?string $filterstartedAteq = null, + ?string $filterstartedAtnq = null, + ?string $filterstartedAtgt = null, + ?string $filterstartedAtlt = null, + ?string $filterstartedAtgte = null, + ?string $filterstartedAtlte = null, + ?string $filterstartedAtcontains = null, + ?string $filterendedAt = null, + ?string $filterendedAteq = null, + ?string $filterendedAtnq = null, + ?string $filterendedAtgt = null, + ?string $filterendedAtlt = null, + ?string $filterendedAtgte = null, + ?string $filterendedAtlte = null, + ?string $filterendedAtcontains = null, + ?string $filterisApproved = null, + ?string $filterapprovedAt = null, + ?string $filterapprovedAteq = null, + ?string $filterapprovedAtnq = null, + ?string $filterapprovedAtgt = null, + ?string $filterapprovedAtlt = null, + ?string $filterapprovedAtgte = null, + ?string $filterapprovedAtlte = null, + ?string $filterapprovedAtcontains = null, + ?string $filterisExported = null, + ): Response { + return $this->connector->send(new GetOvertimes($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); + } } diff --git a/src/Resource/Period.php b/src/Resource/Period.php index 7b47d72..2ad8cb1 100644 --- a/src/Resource/Period.php +++ b/src/Resource/Period.php @@ -8,11 +8,8 @@ class Period extends BaseResource { - /** - * @param string $budget - */ - public function getBudgetPeriods(string $budget): Response - { - return $this->connector->send(new GetBudgetPeriods($budget)); - } + public function getBudgetPeriods(string $budget): Response + { + return $this->connector->send(new GetBudgetPeriods($budget)); + } } diff --git a/src/Resource/TimeSpentTotal.php b/src/Resource/TimeSpentTotal.php index 2dcdcf9..0aeb9b6 100644 --- a/src/Resource/TimeSpentTotal.php +++ b/src/Resource/TimeSpentTotal.php @@ -8,23 +8,14 @@ class TimeSpentTotal extends BaseResource { - /** - * @param string $filterstartedAtgte - * @param string $filterstartedAtlte - * @param string $filterteamId - * @param string $filterteamIdeq - * @param string $filteruserId - * @param string $filteruserIdeq - */ - public function getTimeSpentTotals( - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterteamId = null, - ?string $filterteamIdeq = null, - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ): Response - { - return $this->connector->send(new GetTimeSpentTotals($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); - } + public function getTimeSpentTotals( + ?string $filterstartedAtgte = null, + ?string $filterstartedAtlte = null, + ?string $filterteamId = null, + ?string $filterteamIdeq = null, + ?string $filteruserId = null, + ?string $filteruserIdeq = null, + ): Response { + return $this->connector->send(new GetTimeSpentTotals($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); + } } diff --git a/src/Resource/UserCustomerHoursAggregate.php b/src/Resource/UserCustomerHoursAggregate.php index b9653d6..2449903 100644 --- a/src/Resource/UserCustomerHoursAggregate.php +++ b/src/Resource/UserCustomerHoursAggregate.php @@ -8,75 +8,40 @@ class UserCustomerHoursAggregate extends BaseResource { - /** - * @param string $filterstartedAt - * @param string $filterstartedAteq - * @param string $filterstartedAtnq - * @param string $filterstartedAtgt - * @param string $filterstartedAtlt - * @param string $filterstartedAtgte - * @param string $filterstartedAtlte - * @param string $filterstartedAtcontains - * @param string $filterendedAt - * @param string $filterendedAteq - * @param string $filterendedAtnq - * @param string $filterendedAtgt - * @param string $filterendedAtlt - * @param string $filterendedAtgte - * @param string $filterendedAtlte - * @param string $filterendedAtcontains - * @param string $filterteamId - * @param string $filterteamIdeq - * @param string $filterteamIdnq - * @param string $filterteamIdgt - * @param string $filterteamIdlt - * @param string $filterteamIdgte - * @param string $filterteamIdlte - * @param string $filterteamIdcontains - * @param string $filteruserId - * @param string $filteruserIdeq - * @param string $filteruserIdnq - * @param string $filteruserIdgt - * @param string $filteruserIdlt - * @param string $filteruserIdgte - * @param string $filteruserIdlte - * @param string $filteruserIdcontains - */ - public function getUserCustomerHoursAggregates( - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterteamId = null, - ?string $filterteamIdeq = null, - ?string $filterteamIdnq = null, - ?string $filterteamIdgt = null, - ?string $filterteamIdlt = null, - ?string $filterteamIdgte = null, - ?string $filterteamIdlte = null, - ?string $filterteamIdcontains = null, - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ?string $filteruserIdnq = null, - ?string $filteruserIdgt = null, - ?string $filteruserIdlt = null, - ?string $filteruserIdgte = null, - ?string $filteruserIdlte = null, - ?string $filteruserIdcontains = null, - ): Response - { - return $this->connector->send(new GetUserCustomerHoursAggregates($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); - } + public function getUserCustomerHoursAggregates( + ?string $filterstartedAt = null, + ?string $filterstartedAteq = null, + ?string $filterstartedAtnq = null, + ?string $filterstartedAtgt = null, + ?string $filterstartedAtlt = null, + ?string $filterstartedAtgte = null, + ?string $filterstartedAtlte = null, + ?string $filterstartedAtcontains = null, + ?string $filterendedAt = null, + ?string $filterendedAteq = null, + ?string $filterendedAtnq = null, + ?string $filterendedAtgt = null, + ?string $filterendedAtlt = null, + ?string $filterendedAtgte = null, + ?string $filterendedAtlte = null, + ?string $filterendedAtcontains = null, + ?string $filterteamId = null, + ?string $filterteamIdeq = null, + ?string $filterteamIdnq = null, + ?string $filterteamIdgt = null, + ?string $filterteamIdlt = null, + ?string $filterteamIdgte = null, + ?string $filterteamIdlte = null, + ?string $filterteamIdcontains = null, + ?string $filteruserId = null, + ?string $filteruserIdeq = null, + ?string $filteruserIdnq = null, + ?string $filteruserIdgt = null, + ?string $filteruserIdlt = null, + ?string $filteruserIdgte = null, + ?string $filteruserIdlte = null, + ?string $filteruserIdcontains = null, + ): Response { + return $this->connector->send(new GetUserCustomerHoursAggregates($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); + } } diff --git a/tests/JsonApiBodyTest.php b/tests/JsonApiBodyTest.php index 3bbe6a5..6834671 100644 --- a/tests/JsonApiBodyTest.php +++ b/tests/JsonApiBodyTest.php @@ -31,18 +31,18 @@ $body = $request->body()->all(); // Check for JSON:API 'data' wrapper - if (!isset($body['data'])) { + if (! isset($body['data'])) { return false; } $data = $body['data']; // Validate required JSON:API fields - if (!isset($data['type'])) { + if (! isset($data['type'])) { return false; } - if (!isset($data['attributes'])) { + if (! isset($data['attributes'])) { return false; } From af8f00d92da488a5643e2e4bdc8e13b297f1351e Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 09:56:21 +0100 Subject: [PATCH 11/62] remove put requests --- CLAUDE.md | 10 +++++++ README.md | 11 ++++++++ generator/JsonApiRequestGenerator.php | 9 +++++-- generator/generate.php | 4 +-- src/Requests/Budget/PutBudget.php | 32 ----------------------- src/Requests/Correction/PutCorrection.php | 32 ----------------------- src/Requests/Customer/PutCustomer.php | 32 ----------------------- src/Requests/Entry/PutEntry.php | 32 ----------------------- src/Requests/Team/PutTeam.php | 32 ----------------------- src/Requests/User/PutUser.php | 32 ----------------------- 10 files changed, 30 insertions(+), 196 deletions(-) delete mode 100644 src/Requests/Budget/PutBudget.php delete mode 100644 src/Requests/Correction/PutCorrection.php delete mode 100644 src/Requests/Customer/PutCustomer.php delete mode 100644 src/Requests/Entry/PutEntry.php delete mode 100644 src/Requests/Team/PutTeam.php delete mode 100644 src/Requests/User/PutUser.php diff --git a/CLAUDE.md b/CLAUDE.md index d53e9dc..d87d65c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -262,6 +262,16 @@ protected function isAccessible(User $user, ?string $path = null): bool This SDK is automatically generated from the OpenAPI specification. **DO NOT manually edit auto-generated files**. +### HTTP Methods Policy + +This SDK **does not support PUT requests**. Only the following HTTP methods are supported: +- **POST** - Create new resources +- **PATCH** - Partially update existing resources +- **GET** - Retrieve resources +- **DELETE** - Remove resources + +PUT is intentionally excluded as PATCH provides better semantics for partial updates in JSON:API applications. The generator will throw an exception if it encounters PUT endpoints. + ### āŒ NEVER EDIT THESE DIRECTORIES: - **`src/Dto/`** - All Model/DTO classes (auto-generated with flattened JSON:API attributes) - **`src/Requests/`** - All Request classes (auto-generated from OpenAPI endpoints) diff --git a/README.md b/README.md index 2bbade5..17172c8 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,17 @@ foreach ($included as $resource) { } ``` +## HTTP Methods + +This SDK follows REST best practices and **does not support PUT requests**. Instead: + +- **POST** - Create new resources +- **PATCH** - Partially update existing resources +- **GET** - Retrieve resources +- **DELETE** - Remove resources + +PUT is intentionally excluded because resources are never completely replaced by Timatic. + ## Available Resources The SDK provides access to the following resources: diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index add11f0..d636adb 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -15,6 +15,11 @@ class JsonApiRequestGenerator extends RequestGenerator { protected function generateRequestClass(Endpoint $endpoint): PhpFile { + // Skip PUT requests - we only support POST (create) and PATCH (update) + if ($endpoint->method->isPut()) { + throw new \RuntimeException('PUT requests are not supported in Timatic. Use POST for create and PATCH for update.'); + } + // Use parent generation for most of the class $phpFile = parent::generateRequestClass($endpoint); @@ -41,9 +46,9 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile protected function isMutationRequest(Endpoint $endpoint): bool { + // Only POST and PATCH are supported mutation methods return $endpoint->method->isPost() - || $endpoint->method->isPatch() - || $endpoint->method->isPut(); + || $endpoint->method->isPatch(); } protected function isCollectionRequest(Endpoint $endpoint): bool diff --git a/generator/generate.php b/generator/generate.php index e052c36..09613ce 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -98,8 +98,8 @@ function writeFile($file, $outputDir, $namespace) foreach ($classType->getMethods() as $method) { $methodName = $method->getName(); - // Check if it's a mutation method (post/put/patch) - if (preg_match('/^(post|put|patch)/i', $methodName)) { + // Check if it's a mutation method (post/patch only, PUT is not supported) + if (preg_match('/^(post|patch)/i', $methodName)) { // Add data parameter $method->addParameter('data') ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') diff --git a/src/Requests/Budget/PutBudget.php b/src/Requests/Budget/PutBudget.php deleted file mode 100644 index b4ccdbb..0000000 --- a/src/Requests/Budget/PutBudget.php +++ /dev/null @@ -1,32 +0,0 @@ -budget}"; - } - - public function __construct( - protected string $budget, - protected Model|array $data, - ) {} - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } -} diff --git a/src/Requests/Correction/PutCorrection.php b/src/Requests/Correction/PutCorrection.php deleted file mode 100644 index eae72b2..0000000 --- a/src/Requests/Correction/PutCorrection.php +++ /dev/null @@ -1,32 +0,0 @@ -correction}"; - } - - public function __construct( - protected string $correction, - protected Model|array $data, - ) {} - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } -} diff --git a/src/Requests/Customer/PutCustomer.php b/src/Requests/Customer/PutCustomer.php deleted file mode 100644 index c7672a2..0000000 --- a/src/Requests/Customer/PutCustomer.php +++ /dev/null @@ -1,32 +0,0 @@ -customer}"; - } - - public function __construct( - protected string $customer, - protected Model|array $data, - ) {} - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } -} diff --git a/src/Requests/Entry/PutEntry.php b/src/Requests/Entry/PutEntry.php deleted file mode 100644 index 0c4e467..0000000 --- a/src/Requests/Entry/PutEntry.php +++ /dev/null @@ -1,32 +0,0 @@ -entry}"; - } - - public function __construct( - protected string $entry, - protected Model|array $data, - ) {} - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } -} diff --git a/src/Requests/Team/PutTeam.php b/src/Requests/Team/PutTeam.php deleted file mode 100644 index e581007..0000000 --- a/src/Requests/Team/PutTeam.php +++ /dev/null @@ -1,32 +0,0 @@ -team}"; - } - - public function __construct( - protected string $team, - protected Model|array $data, - ) {} - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } -} diff --git a/src/Requests/User/PutUser.php b/src/Requests/User/PutUser.php deleted file mode 100644 index 4133fe7..0000000 --- a/src/Requests/User/PutUser.php +++ /dev/null @@ -1,32 +0,0 @@ -user}"; - } - - public function __construct( - protected string $user, - protected Model|array $data, - ) {} - - protected function defaultBody(): array - { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; - } -} From 0dbd2cb41be87abd286cc0b3659ec080c900aa32 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 18:11:11 +0100 Subject: [PATCH 12/62] clean up before generate --- generator/JsonApiRequestGenerator.php | 14 +++++++++----- generator/generate.php | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index d636adb..10575a0 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -27,6 +27,12 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile $namespace = array_values($phpFile->getNamespaces())[0]; $classType = array_values($namespace->getClasses())[0]; + // Add "Request" suffix to class name + $originalName = $classType->getName(); + if (! str_ends_with($originalName, 'Request')) { + $classType->setName($originalName.'Request'); + } + // Add Model import $namespace->addUse(Model::class); @@ -68,17 +74,15 @@ protected function addModelDataParameter(ClassType $classType, $namespace): void // Get constructor $constructor = $classType->getMethod('__construct'); - // Add data parameter with fully qualified type + // Add data parameter with Model-only type (no array support) $constructor->addPromotedParameter('data') - ->setType('\\Timatic\\SDK\\Foundation\\Model|array') + ->setType('\\Timatic\\SDK\\Foundation\\Model') ->setProtected(); // Add defaultBody method $defaultBody = $classType->addMethod('defaultBody') ->setProtected() ->setReturnType('array') - ->addBody('return $this->data instanceof Model') - ->addBody(' ? $this->data->toJsonApi()') - ->addBody(' : [\'data\' => $this->data];'); + ->addBody('return $this->data->toJsonApi();'); } } diff --git a/generator/generate.php b/generator/generate.php index 09613ce..d3dd987 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -31,6 +31,33 @@ file_put_contents(__DIR__.'/../openapi.json', $openApiJson); echo "āœ… OpenAPI specification downloaded\n\n"; +// Clean up previously generated folders +echo "🧹 Cleaning up previously generated files...\n"; +$foldersToClean = [ + __DIR__.'/../src/Requests', + __DIR__.'/../src/Resource', + __DIR__.'/../src/Dto', +]; + +foreach ($foldersToClean as $folder) { + if (is_dir($folder)) { + // Recursively delete directory + $files = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($files as $fileinfo) { + $todo = ($fileinfo->isDir() ? 'rmdir' : 'unlink'); + $todo($fileinfo->getRealPath()); + } + + rmdir($folder); + echo ' āœ“ Removed '.basename($folder)."\n"; + } +} +echo "āœ… Cleanup completed\n\n"; + // Parse the specification echo "šŸ”Ø Parsing OpenAPI specification...\n"; $specification = Factory::parse('openapi', __DIR__.'/../openapi.json'); From 6f33139dec81a13028fb5c9731bbcc1eba1d5195 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 20:18:14 +0100 Subject: [PATCH 13/62] request postfix --- ...OvertimeApprove.php => PostOvertimeApproveRequest.php} | 8 +++----- .../Budget/{DeleteBudget.php => DeleteBudgetRequest.php} | 2 +- .../Budget/{GetBudget.php => GetBudgetRequest.php} | 2 +- .../Budget/{GetBudgets.php => GetBudgetsRequest.php} | 2 +- .../Budget/{PatchBudget.php => PatchBudgetRequest.php} | 8 +++----- .../Budget/{PostBudgets.php => PostBudgetsRequest.php} | 8 +++----- ...pentTotals.php => GetBudgetTimeSpentTotalsRequest.php} | 2 +- .../{GetBudgetTypes.php => GetBudgetTypesRequest.php} | 2 +- .../Change/{GetChange.php => GetChangeRequest.php} | 2 +- .../Change/{GetChanges.php => GetChangesRequest.php} | 2 +- .../{PatchCorrection.php => PatchCorrectionRequest.php} | 8 +++----- .../{PostCorrections.php => PostCorrectionsRequest.php} | 8 +++----- .../{DeleteCustomer.php => DeleteCustomerRequest.php} | 2 +- .../Customer/{GetCustomer.php => GetCustomerRequest.php} | 2 +- .../{GetCustomers.php => GetCustomersRequest.php} | 2 +- .../{PatchCustomer.php => PatchCustomerRequest.php} | 8 +++----- .../{PostCustomers.php => PostCustomersRequest.php} | 8 +++----- ...tDailyProgresses.php => GetDailyProgressesRequest.php} | 2 +- ...ntriesExport.php => GetBudgetEntriesExportRequest.php} | 2 +- .../Entry/{DeleteEntry.php => DeleteEntryRequest.php} | 2 +- .../Entry/{GetEntries.php => GetEntriesRequest.php} | 2 +- src/Requests/Entry/{GetEntry.php => GetEntryRequest.php} | 2 +- .../Entry/{PatchEntry.php => PatchEntryRequest.php} | 8 +++----- .../Entry/{PostEntries.php => PostEntriesRequest.php} | 8 +++----- ...trySuggestion.php => DeleteEntrySuggestionRequest.php} | 2 +- ...tEntrySuggestion.php => GetEntrySuggestionRequest.php} | 2 +- ...ntrySuggestions.php => GetEntrySuggestionsRequest.php} | 2 +- .../Event/{PostEvents.php => PostEventsRequest.php} | 8 +++----- ...tsExportMails.php => GetBudgetsExportMailsRequest.php} | 2 +- .../Incident/{GetIncident.php => GetIncidentRequest.php} | 2 +- .../{GetIncidents.php => GetIncidentsRequest.php} | 2 +- ...Exported.php => PostOvertimeMarkAsExportedRequest.php} | 8 +++----- ...kAsInvoiced.php => PostEntryMarkAsInvoicedRequest.php} | 8 +++----- src/Requests/Me/{GetMes.php => GetMesRequest.php} | 2 +- ...tIncidentsNumber.php => GetIncidentsNumberRequest.php} | 2 +- .../{GetOvertimes.php => GetOvertimesRequest.php} | 2 +- .../{GetBudgetPeriods.php => GetBudgetPeriodsRequest.php} | 2 +- .../Team/{DeleteTeam.php => DeleteTeamRequest.php} | 2 +- src/Requests/Team/{GetTeam.php => GetTeamRequest.php} | 2 +- src/Requests/Team/{GetTeams.php => GetTeamsRequest.php} | 2 +- src/Requests/Team/{PatchTeam.php => PatchTeamRequest.php} | 8 +++----- src/Requests/Team/{PostTeams.php => PostTeamsRequest.php} | 8 +++----- ...tTimeSpentTotals.php => GetTimeSpentTotalsRequest.php} | 2 +- .../User/{DeleteUser.php => DeleteUserRequest.php} | 2 +- src/Requests/User/{GetUser.php => GetUserRequest.php} | 2 +- src/Requests/User/{GetUsers.php => GetUsersRequest.php} | 2 +- src/Requests/User/{PatchUser.php => PatchUserRequest.php} | 8 +++----- src/Requests/User/{PostUsers.php => PostUsersRequest.php} | 8 +++----- ...ates.php => GetUserCustomerHoursAggregatesRequest.php} | 2 +- 49 files changed, 81 insertions(+), 113 deletions(-) rename src/Requests/Approve/{PostOvertimeApprove.php => PostOvertimeApproveRequest.php} (71%) rename src/Requests/Budget/{DeleteBudget.php => DeleteBudgetRequest.php} (89%) rename src/Requests/Budget/{GetBudget.php => GetBudgetRequest.php} (89%) rename src/Requests/Budget/{GetBudgets.php => GetBudgetsRequest.php} (97%) rename src/Requests/Budget/{PatchBudget.php => PatchBudgetRequest.php} (71%) rename src/Requests/Budget/{PostBudgets.php => PostBudgetsRequest.php} (69%) rename src/Requests/BudgetTimeSpentTotal/{GetBudgetTimeSpentTotals.php => GetBudgetTimeSpentTotalsRequest.php} (89%) rename src/Requests/BudgetType/{GetBudgetTypes.php => GetBudgetTypesRequest.php} (84%) rename src/Requests/Change/{GetChange.php => GetChangeRequest.php} (89%) rename src/Requests/Change/{GetChanges.php => GetChangesRequest.php} (84%) rename src/Requests/Correction/{PatchCorrection.php => PatchCorrectionRequest.php} (71%) rename src/Requests/Correction/{PostCorrections.php => PostCorrectionsRequest.php} (69%) rename src/Requests/Customer/{DeleteCustomer.php => DeleteCustomerRequest.php} (89%) rename src/Requests/Customer/{GetCustomer.php => GetCustomerRequest.php} (89%) rename src/Requests/Customer/{GetCustomers.php => GetCustomersRequest.php} (90%) rename src/Requests/Customer/{PatchCustomer.php => PatchCustomerRequest.php} (71%) rename src/Requests/Customer/{PostCustomers.php => PostCustomersRequest.php} (69%) rename src/Requests/DailyProgress/{GetDailyProgresses.php => GetDailyProgressesRequest.php} (83%) rename src/Requests/EntriesExport/{GetBudgetEntriesExport.php => GetBudgetEntriesExportRequest.php} (99%) rename src/Requests/Entry/{DeleteEntry.php => DeleteEntryRequest.php} (89%) rename src/Requests/Entry/{GetEntries.php => GetEntriesRequest.php} (99%) rename src/Requests/Entry/{GetEntry.php => GetEntryRequest.php} (90%) rename src/Requests/Entry/{PatchEntry.php => PatchEntryRequest.php} (71%) rename src/Requests/Entry/{PostEntries.php => PostEntriesRequest.php} (69%) rename src/Requests/EntrySuggestion/{DeleteEntrySuggestion.php => DeleteEntrySuggestionRequest.php} (88%) rename src/Requests/EntrySuggestion/{GetEntrySuggestion.php => GetEntrySuggestionRequest.php} (89%) rename src/Requests/EntrySuggestion/{GetEntrySuggestions.php => GetEntrySuggestionsRequest.php} (94%) rename src/Requests/Event/{PostEvents.php => PostEventsRequest.php} (69%) rename src/Requests/ExportMail/{GetBudgetsExportMails.php => GetBudgetsExportMailsRequest.php} (83%) rename src/Requests/Incident/{GetIncident.php => GetIncidentRequest.php} (89%) rename src/Requests/Incident/{GetIncidents.php => GetIncidentsRequest.php} (84%) rename src/Requests/MarkAsExported/{PostOvertimeMarkAsExported.php => PostOvertimeMarkAsExportedRequest.php} (71%) rename src/Requests/MarkAsInvoiced/{PostEntryMarkAsInvoiced.php => PostEntryMarkAsInvoicedRequest.php} (71%) rename src/Requests/Me/{GetMes.php => GetMesRequest.php} (84%) rename src/Requests/Number/{GetIncidentsNumber.php => GetIncidentsNumberRequest.php} (88%) rename src/Requests/Overtime/{GetOvertimes.php => GetOvertimesRequest.php} (98%) rename src/Requests/Period/{GetBudgetPeriods.php => GetBudgetPeriodsRequest.php} (88%) rename src/Requests/Team/{DeleteTeam.php => DeleteTeamRequest.php} (89%) rename src/Requests/Team/{GetTeam.php => GetTeamRequest.php} (90%) rename src/Requests/Team/{GetTeams.php => GetTeamsRequest.php} (84%) rename src/Requests/Team/{PatchTeam.php => PatchTeamRequest.php} (71%) rename src/Requests/Team/{PostTeams.php => PostTeamsRequest.php} (69%) rename src/Requests/TimeSpentTotal/{GetTimeSpentTotals.php => GetTimeSpentTotalsRequest.php} (94%) rename src/Requests/User/{DeleteUser.php => DeleteUserRequest.php} (89%) rename src/Requests/User/{GetUser.php => GetUserRequest.php} (90%) rename src/Requests/User/{GetUsers.php => GetUsersRequest.php} (91%) rename src/Requests/User/{PatchUser.php => PatchUserRequest.php} (71%) rename src/Requests/User/{PostUsers.php => PostUsersRequest.php} (69%) rename src/Requests/UserCustomerHoursAggregate/{GetUserCustomerHoursAggregates.php => GetUserCustomerHoursAggregatesRequest.php} (98%) diff --git a/src/Requests/Approve/PostOvertimeApprove.php b/src/Requests/Approve/PostOvertimeApproveRequest.php similarity index 71% rename from src/Requests/Approve/PostOvertimeApprove.php rename to src/Requests/Approve/PostOvertimeApproveRequest.php index b542c99..32eda71 100644 --- a/src/Requests/Approve/PostOvertimeApprove.php +++ b/src/Requests/Approve/PostOvertimeApproveRequest.php @@ -11,7 +11,7 @@ /** * postOvertimeApprove */ -class PostOvertimeApprove extends Request implements HasBody +class PostOvertimeApproveRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $overtime, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Budget/DeleteBudget.php b/src/Requests/Budget/DeleteBudgetRequest.php similarity index 89% rename from src/Requests/Budget/DeleteBudget.php rename to src/Requests/Budget/DeleteBudgetRequest.php index d50969f..5b0943b 100644 --- a/src/Requests/Budget/DeleteBudget.php +++ b/src/Requests/Budget/DeleteBudgetRequest.php @@ -8,7 +8,7 @@ /** * deleteBudget */ -class DeleteBudget extends Request +class DeleteBudgetRequest extends Request { protected Method $method = Method::DELETE; diff --git a/src/Requests/Budget/GetBudget.php b/src/Requests/Budget/GetBudgetRequest.php similarity index 89% rename from src/Requests/Budget/GetBudget.php rename to src/Requests/Budget/GetBudgetRequest.php index e9fb3bb..b0f2ccd 100644 --- a/src/Requests/Budget/GetBudget.php +++ b/src/Requests/Budget/GetBudgetRequest.php @@ -8,7 +8,7 @@ /** * getBudget */ -class GetBudget extends Request +class GetBudgetRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Budget/GetBudgets.php b/src/Requests/Budget/GetBudgetsRequest.php similarity index 97% rename from src/Requests/Budget/GetBudgets.php rename to src/Requests/Budget/GetBudgetsRequest.php index df0023f..28f507d 100644 --- a/src/Requests/Budget/GetBudgets.php +++ b/src/Requests/Budget/GetBudgetsRequest.php @@ -9,7 +9,7 @@ /** * getBudgets */ -class GetBudgets extends Request implements Paginatable +class GetBudgetsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Budget/PatchBudget.php b/src/Requests/Budget/PatchBudgetRequest.php similarity index 71% rename from src/Requests/Budget/PatchBudget.php rename to src/Requests/Budget/PatchBudgetRequest.php index 203f428..bef29f8 100644 --- a/src/Requests/Budget/PatchBudget.php +++ b/src/Requests/Budget/PatchBudgetRequest.php @@ -11,7 +11,7 @@ /** * patchBudget */ -class PatchBudget extends Request implements HasBody +class PatchBudgetRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $budget, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Budget/PostBudgets.php b/src/Requests/Budget/PostBudgetsRequest.php similarity index 69% rename from src/Requests/Budget/PostBudgets.php rename to src/Requests/Budget/PostBudgetsRequest.php index 75043f6..ffd7569 100644 --- a/src/Requests/Budget/PostBudgets.php +++ b/src/Requests/Budget/PostBudgetsRequest.php @@ -11,7 +11,7 @@ /** * postBudgets */ -class PostBudgets extends Request implements HasBody +class PostBudgetsRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php similarity index 89% rename from src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php rename to src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php index f11b2d2..c624f53 100644 --- a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotals.php +++ b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php @@ -9,7 +9,7 @@ /** * getBudgetTimeSpentTotals */ -class GetBudgetTimeSpentTotals extends Request implements Paginatable +class GetBudgetTimeSpentTotalsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/BudgetType/GetBudgetTypes.php b/src/Requests/BudgetType/GetBudgetTypesRequest.php similarity index 84% rename from src/Requests/BudgetType/GetBudgetTypes.php rename to src/Requests/BudgetType/GetBudgetTypesRequest.php index 693c509..b3a8f24 100644 --- a/src/Requests/BudgetType/GetBudgetTypes.php +++ b/src/Requests/BudgetType/GetBudgetTypesRequest.php @@ -9,7 +9,7 @@ /** * getBudgetTypes */ -class GetBudgetTypes extends Request implements Paginatable +class GetBudgetTypesRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Change/GetChange.php b/src/Requests/Change/GetChangeRequest.php similarity index 89% rename from src/Requests/Change/GetChange.php rename to src/Requests/Change/GetChangeRequest.php index 112ccbb..29eee20 100644 --- a/src/Requests/Change/GetChange.php +++ b/src/Requests/Change/GetChangeRequest.php @@ -8,7 +8,7 @@ /** * getChange */ -class GetChange extends Request +class GetChangeRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Change/GetChanges.php b/src/Requests/Change/GetChangesRequest.php similarity index 84% rename from src/Requests/Change/GetChanges.php rename to src/Requests/Change/GetChangesRequest.php index 9239f24..9a26355 100644 --- a/src/Requests/Change/GetChanges.php +++ b/src/Requests/Change/GetChangesRequest.php @@ -9,7 +9,7 @@ /** * getChanges */ -class GetChanges extends Request implements Paginatable +class GetChangesRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Correction/PatchCorrection.php b/src/Requests/Correction/PatchCorrectionRequest.php similarity index 71% rename from src/Requests/Correction/PatchCorrection.php rename to src/Requests/Correction/PatchCorrectionRequest.php index 3b206dd..f234b54 100644 --- a/src/Requests/Correction/PatchCorrection.php +++ b/src/Requests/Correction/PatchCorrectionRequest.php @@ -11,7 +11,7 @@ /** * patchCorrection */ -class PatchCorrection extends Request implements HasBody +class PatchCorrectionRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $correction, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Correction/PostCorrections.php b/src/Requests/Correction/PostCorrectionsRequest.php similarity index 69% rename from src/Requests/Correction/PostCorrections.php rename to src/Requests/Correction/PostCorrectionsRequest.php index 63f6048..f574882 100644 --- a/src/Requests/Correction/PostCorrections.php +++ b/src/Requests/Correction/PostCorrectionsRequest.php @@ -11,7 +11,7 @@ /** * postCorrections */ -class PostCorrections extends Request implements HasBody +class PostCorrectionsRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Customer/DeleteCustomer.php b/src/Requests/Customer/DeleteCustomerRequest.php similarity index 89% rename from src/Requests/Customer/DeleteCustomer.php rename to src/Requests/Customer/DeleteCustomerRequest.php index a3c7684..1dd7c24 100644 --- a/src/Requests/Customer/DeleteCustomer.php +++ b/src/Requests/Customer/DeleteCustomerRequest.php @@ -8,7 +8,7 @@ /** * deleteCustomer */ -class DeleteCustomer extends Request +class DeleteCustomerRequest extends Request { protected Method $method = Method::DELETE; diff --git a/src/Requests/Customer/GetCustomer.php b/src/Requests/Customer/GetCustomerRequest.php similarity index 89% rename from src/Requests/Customer/GetCustomer.php rename to src/Requests/Customer/GetCustomerRequest.php index efad783..0d32f0e 100644 --- a/src/Requests/Customer/GetCustomer.php +++ b/src/Requests/Customer/GetCustomerRequest.php @@ -8,7 +8,7 @@ /** * getCustomer */ -class GetCustomer extends Request +class GetCustomerRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Customer/GetCustomers.php b/src/Requests/Customer/GetCustomersRequest.php similarity index 90% rename from src/Requests/Customer/GetCustomers.php rename to src/Requests/Customer/GetCustomersRequest.php index 4b72f3f..9930b80 100644 --- a/src/Requests/Customer/GetCustomers.php +++ b/src/Requests/Customer/GetCustomersRequest.php @@ -9,7 +9,7 @@ /** * getCustomers */ -class GetCustomers extends Request implements Paginatable +class GetCustomersRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Customer/PatchCustomer.php b/src/Requests/Customer/PatchCustomerRequest.php similarity index 71% rename from src/Requests/Customer/PatchCustomer.php rename to src/Requests/Customer/PatchCustomerRequest.php index aef2076..a616dc2 100644 --- a/src/Requests/Customer/PatchCustomer.php +++ b/src/Requests/Customer/PatchCustomerRequest.php @@ -11,7 +11,7 @@ /** * patchCustomer */ -class PatchCustomer extends Request implements HasBody +class PatchCustomerRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $customer, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Customer/PostCustomers.php b/src/Requests/Customer/PostCustomersRequest.php similarity index 69% rename from src/Requests/Customer/PostCustomers.php rename to src/Requests/Customer/PostCustomersRequest.php index db51e34..05bf5b1 100644 --- a/src/Requests/Customer/PostCustomers.php +++ b/src/Requests/Customer/PostCustomersRequest.php @@ -11,7 +11,7 @@ /** * postCustomers */ -class PostCustomers extends Request implements HasBody +class PostCustomersRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/DailyProgress/GetDailyProgresses.php b/src/Requests/DailyProgress/GetDailyProgressesRequest.php similarity index 83% rename from src/Requests/DailyProgress/GetDailyProgresses.php rename to src/Requests/DailyProgress/GetDailyProgressesRequest.php index 0e8f5b2..3a4461a 100644 --- a/src/Requests/DailyProgress/GetDailyProgresses.php +++ b/src/Requests/DailyProgress/GetDailyProgressesRequest.php @@ -9,7 +9,7 @@ /** * getDailyProgresses */ -class GetDailyProgresses extends Request implements Paginatable +class GetDailyProgressesRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/EntriesExport/GetBudgetEntriesExport.php b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php similarity index 99% rename from src/Requests/EntriesExport/GetBudgetEntriesExport.php rename to src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php index d29eb92..c65e263 100644 --- a/src/Requests/EntriesExport/GetBudgetEntriesExport.php +++ b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php @@ -8,7 +8,7 @@ /** * getBudgetEntriesExport */ -class GetBudgetEntriesExport extends Request +class GetBudgetEntriesExportRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Entry/DeleteEntry.php b/src/Requests/Entry/DeleteEntryRequest.php similarity index 89% rename from src/Requests/Entry/DeleteEntry.php rename to src/Requests/Entry/DeleteEntryRequest.php index 9d986a2..5ef24c9 100644 --- a/src/Requests/Entry/DeleteEntry.php +++ b/src/Requests/Entry/DeleteEntryRequest.php @@ -8,7 +8,7 @@ /** * deleteEntry */ -class DeleteEntry extends Request +class DeleteEntryRequest extends Request { protected Method $method = Method::DELETE; diff --git a/src/Requests/Entry/GetEntries.php b/src/Requests/Entry/GetEntriesRequest.php similarity index 99% rename from src/Requests/Entry/GetEntries.php rename to src/Requests/Entry/GetEntriesRequest.php index a2be396..652aa8a 100644 --- a/src/Requests/Entry/GetEntries.php +++ b/src/Requests/Entry/GetEntriesRequest.php @@ -9,7 +9,7 @@ /** * getEntries */ -class GetEntries extends Request implements Paginatable +class GetEntriesRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Entry/GetEntry.php b/src/Requests/Entry/GetEntryRequest.php similarity index 90% rename from src/Requests/Entry/GetEntry.php rename to src/Requests/Entry/GetEntryRequest.php index f8aac90..12af9c7 100644 --- a/src/Requests/Entry/GetEntry.php +++ b/src/Requests/Entry/GetEntryRequest.php @@ -8,7 +8,7 @@ /** * getEntry */ -class GetEntry extends Request +class GetEntryRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Entry/PatchEntry.php b/src/Requests/Entry/PatchEntryRequest.php similarity index 71% rename from src/Requests/Entry/PatchEntry.php rename to src/Requests/Entry/PatchEntryRequest.php index 80a18df..a0e174c 100644 --- a/src/Requests/Entry/PatchEntry.php +++ b/src/Requests/Entry/PatchEntryRequest.php @@ -11,7 +11,7 @@ /** * patchEntry */ -class PatchEntry extends Request implements HasBody +class PatchEntryRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $entry, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Entry/PostEntries.php b/src/Requests/Entry/PostEntriesRequest.php similarity index 69% rename from src/Requests/Entry/PostEntries.php rename to src/Requests/Entry/PostEntriesRequest.php index 79f2d26..b6fcad6 100644 --- a/src/Requests/Entry/PostEntries.php +++ b/src/Requests/Entry/PostEntriesRequest.php @@ -11,7 +11,7 @@ /** * postEntries */ -class PostEntries extends Request implements HasBody +class PostEntriesRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php b/src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php similarity index 88% rename from src/Requests/EntrySuggestion/DeleteEntrySuggestion.php rename to src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php index c0b2bf2..94501ca 100644 --- a/src/Requests/EntrySuggestion/DeleteEntrySuggestion.php +++ b/src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php @@ -8,7 +8,7 @@ /** * deleteEntrySuggestion */ -class DeleteEntrySuggestion extends Request +class DeleteEntrySuggestionRequest extends Request { protected Method $method = Method::DELETE; diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestion.php b/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php similarity index 89% rename from src/Requests/EntrySuggestion/GetEntrySuggestion.php rename to src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php index 731de98..d127024 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestion.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php @@ -8,7 +8,7 @@ /** * getEntrySuggestion */ -class GetEntrySuggestion extends Request +class GetEntrySuggestionRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestions.php b/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php similarity index 94% rename from src/Requests/EntrySuggestion/GetEntrySuggestions.php rename to src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php index e283752..89b631c 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestions.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php @@ -9,7 +9,7 @@ /** * getEntrySuggestions */ -class GetEntrySuggestions extends Request implements Paginatable +class GetEntrySuggestionsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Event/PostEvents.php b/src/Requests/Event/PostEventsRequest.php similarity index 69% rename from src/Requests/Event/PostEvents.php rename to src/Requests/Event/PostEventsRequest.php index 5db188b..dab7d73 100644 --- a/src/Requests/Event/PostEvents.php +++ b/src/Requests/Event/PostEventsRequest.php @@ -11,7 +11,7 @@ /** * postEvents */ -class PostEvents extends Request implements HasBody +class PostEventsRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/ExportMail/GetBudgetsExportMails.php b/src/Requests/ExportMail/GetBudgetsExportMailsRequest.php similarity index 83% rename from src/Requests/ExportMail/GetBudgetsExportMails.php rename to src/Requests/ExportMail/GetBudgetsExportMailsRequest.php index 2b302c3..80f2676 100644 --- a/src/Requests/ExportMail/GetBudgetsExportMails.php +++ b/src/Requests/ExportMail/GetBudgetsExportMailsRequest.php @@ -9,7 +9,7 @@ /** * getBudgetsExportMails */ -class GetBudgetsExportMails extends Request implements Paginatable +class GetBudgetsExportMailsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Incident/GetIncident.php b/src/Requests/Incident/GetIncidentRequest.php similarity index 89% rename from src/Requests/Incident/GetIncident.php rename to src/Requests/Incident/GetIncidentRequest.php index ccb5999..92e0be3 100644 --- a/src/Requests/Incident/GetIncident.php +++ b/src/Requests/Incident/GetIncidentRequest.php @@ -8,7 +8,7 @@ /** * getIncident */ -class GetIncident extends Request +class GetIncidentRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Incident/GetIncidents.php b/src/Requests/Incident/GetIncidentsRequest.php similarity index 84% rename from src/Requests/Incident/GetIncidents.php rename to src/Requests/Incident/GetIncidentsRequest.php index bdf6792..77770fa 100644 --- a/src/Requests/Incident/GetIncidents.php +++ b/src/Requests/Incident/GetIncidentsRequest.php @@ -9,7 +9,7 @@ /** * getIncidents */ -class GetIncidents extends Request implements Paginatable +class GetIncidentsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php similarity index 71% rename from src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php rename to src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php index 224b6ba..7cb5c49 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExported.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php @@ -11,7 +11,7 @@ /** * postOvertimeMarkAsExported */ -class PostOvertimeMarkAsExported extends Request implements HasBody +class PostOvertimeMarkAsExportedRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $overtime, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php similarity index 71% rename from src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php rename to src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php index f723ff6..da59935 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoiced.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php @@ -11,7 +11,7 @@ /** * postEntryMarkAsInvoiced */ -class PostEntryMarkAsInvoiced extends Request implements HasBody +class PostEntryMarkAsInvoicedRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $entry, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Me/GetMes.php b/src/Requests/Me/GetMesRequest.php similarity index 84% rename from src/Requests/Me/GetMes.php rename to src/Requests/Me/GetMesRequest.php index c9858ac..107fd45 100644 --- a/src/Requests/Me/GetMes.php +++ b/src/Requests/Me/GetMesRequest.php @@ -9,7 +9,7 @@ /** * getMes */ -class GetMes extends Request implements Paginatable +class GetMesRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Number/GetIncidentsNumber.php b/src/Requests/Number/GetIncidentsNumberRequest.php similarity index 88% rename from src/Requests/Number/GetIncidentsNumber.php rename to src/Requests/Number/GetIncidentsNumberRequest.php index 1c2af6d..e96a58a 100644 --- a/src/Requests/Number/GetIncidentsNumber.php +++ b/src/Requests/Number/GetIncidentsNumberRequest.php @@ -8,7 +8,7 @@ /** * getIncidentsNumber */ -class GetIncidentsNumber extends Request +class GetIncidentsNumberRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Overtime/GetOvertimes.php b/src/Requests/Overtime/GetOvertimesRequest.php similarity index 98% rename from src/Requests/Overtime/GetOvertimes.php rename to src/Requests/Overtime/GetOvertimesRequest.php index e06e44e..48a1c85 100644 --- a/src/Requests/Overtime/GetOvertimes.php +++ b/src/Requests/Overtime/GetOvertimesRequest.php @@ -9,7 +9,7 @@ /** * getOvertimes */ -class GetOvertimes extends Request implements Paginatable +class GetOvertimesRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Period/GetBudgetPeriods.php b/src/Requests/Period/GetBudgetPeriodsRequest.php similarity index 88% rename from src/Requests/Period/GetBudgetPeriods.php rename to src/Requests/Period/GetBudgetPeriodsRequest.php index 575a984..960b490 100644 --- a/src/Requests/Period/GetBudgetPeriods.php +++ b/src/Requests/Period/GetBudgetPeriodsRequest.php @@ -8,7 +8,7 @@ /** * getBudgetPeriods */ -class GetBudgetPeriods extends Request +class GetBudgetPeriodsRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Team/DeleteTeam.php b/src/Requests/Team/DeleteTeamRequest.php similarity index 89% rename from src/Requests/Team/DeleteTeam.php rename to src/Requests/Team/DeleteTeamRequest.php index 20f2201..8a48117 100644 --- a/src/Requests/Team/DeleteTeam.php +++ b/src/Requests/Team/DeleteTeamRequest.php @@ -8,7 +8,7 @@ /** * deleteTeam */ -class DeleteTeam extends Request +class DeleteTeamRequest extends Request { protected Method $method = Method::DELETE; diff --git a/src/Requests/Team/GetTeam.php b/src/Requests/Team/GetTeamRequest.php similarity index 90% rename from src/Requests/Team/GetTeam.php rename to src/Requests/Team/GetTeamRequest.php index 8d83ef1..29ecef0 100644 --- a/src/Requests/Team/GetTeam.php +++ b/src/Requests/Team/GetTeamRequest.php @@ -8,7 +8,7 @@ /** * getTeam */ -class GetTeam extends Request +class GetTeamRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/Team/GetTeams.php b/src/Requests/Team/GetTeamsRequest.php similarity index 84% rename from src/Requests/Team/GetTeams.php rename to src/Requests/Team/GetTeamsRequest.php index 172cb2d..eeda391 100644 --- a/src/Requests/Team/GetTeams.php +++ b/src/Requests/Team/GetTeamsRequest.php @@ -9,7 +9,7 @@ /** * getTeams */ -class GetTeams extends Request implements Paginatable +class GetTeamsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/Team/PatchTeam.php b/src/Requests/Team/PatchTeamRequest.php similarity index 71% rename from src/Requests/Team/PatchTeam.php rename to src/Requests/Team/PatchTeamRequest.php index 5576543..b2d29c2 100644 --- a/src/Requests/Team/PatchTeam.php +++ b/src/Requests/Team/PatchTeamRequest.php @@ -11,7 +11,7 @@ /** * patchTeam */ -class PatchTeam extends Request implements HasBody +class PatchTeamRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $team, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/Team/PostTeams.php b/src/Requests/Team/PostTeamsRequest.php similarity index 69% rename from src/Requests/Team/PostTeams.php rename to src/Requests/Team/PostTeamsRequest.php index 9c511c4..6884d76 100644 --- a/src/Requests/Team/PostTeams.php +++ b/src/Requests/Team/PostTeamsRequest.php @@ -11,7 +11,7 @@ /** * postTeams */ -class PostTeams extends Request implements HasBody +class PostTeamsRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php b/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php similarity index 94% rename from src/Requests/TimeSpentTotal/GetTimeSpentTotals.php rename to src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php index 96d32f8..ec3a20b 100644 --- a/src/Requests/TimeSpentTotal/GetTimeSpentTotals.php +++ b/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php @@ -9,7 +9,7 @@ /** * getTimeSpentTotals */ -class GetTimeSpentTotals extends Request implements Paginatable +class GetTimeSpentTotalsRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/User/DeleteUser.php b/src/Requests/User/DeleteUserRequest.php similarity index 89% rename from src/Requests/User/DeleteUser.php rename to src/Requests/User/DeleteUserRequest.php index f0fc71a..1e47686 100644 --- a/src/Requests/User/DeleteUser.php +++ b/src/Requests/User/DeleteUserRequest.php @@ -8,7 +8,7 @@ /** * deleteUser */ -class DeleteUser extends Request +class DeleteUserRequest extends Request { protected Method $method = Method::DELETE; diff --git a/src/Requests/User/GetUser.php b/src/Requests/User/GetUserRequest.php similarity index 90% rename from src/Requests/User/GetUser.php rename to src/Requests/User/GetUserRequest.php index ebcbe5d..e1ceeb7 100644 --- a/src/Requests/User/GetUser.php +++ b/src/Requests/User/GetUserRequest.php @@ -8,7 +8,7 @@ /** * getUser */ -class GetUser extends Request +class GetUserRequest extends Request { protected Method $method = Method::GET; diff --git a/src/Requests/User/GetUsers.php b/src/Requests/User/GetUsersRequest.php similarity index 91% rename from src/Requests/User/GetUsers.php rename to src/Requests/User/GetUsersRequest.php index 2c81309..cfff519 100644 --- a/src/Requests/User/GetUsers.php +++ b/src/Requests/User/GetUsersRequest.php @@ -9,7 +9,7 @@ /** * getUsers */ -class GetUsers extends Request implements Paginatable +class GetUsersRequest extends Request implements Paginatable { protected Method $method = Method::GET; diff --git a/src/Requests/User/PatchUser.php b/src/Requests/User/PatchUserRequest.php similarity index 71% rename from src/Requests/User/PatchUser.php rename to src/Requests/User/PatchUserRequest.php index f7cfcbe..787e346 100644 --- a/src/Requests/User/PatchUser.php +++ b/src/Requests/User/PatchUserRequest.php @@ -11,7 +11,7 @@ /** * patchUser */ -class PatchUser extends Request implements HasBody +class PatchUserRequest extends Request implements HasBody { use HasJsonBody; @@ -24,13 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $user, - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/User/PostUsers.php b/src/Requests/User/PostUsersRequest.php similarity index 69% rename from src/Requests/User/PostUsers.php rename to src/Requests/User/PostUsersRequest.php index 92dc07e..9b24c2d 100644 --- a/src/Requests/User/PostUsers.php +++ b/src/Requests/User/PostUsersRequest.php @@ -11,7 +11,7 @@ /** * postUsers */ -class PostUsers extends Request implements HasBody +class PostUsersRequest extends Request implements HasBody { use HasJsonBody; @@ -23,13 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model|array $data, + protected Model $data, ) {} protected function defaultBody(): array { - return $this->data instanceof Model - ? $this->data->toJsonApi() - : ['data' => $this->data]; + return $this->data->toJsonApi(); } } diff --git a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php similarity index 98% rename from src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php rename to src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php index 9d190c8..d205bf8 100644 --- a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregates.php +++ b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php @@ -9,7 +9,7 @@ /** * getUserCustomerHoursAggregates */ -class GetUserCustomerHoursAggregates extends Request implements Paginatable +class GetUserCustomerHoursAggregatesRequest extends Request implements Paginatable { protected Method $method = Method::GET; From 7f1f4a8a44000895abca9be99e5248a924233cc4 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 20:18:56 +0100 Subject: [PATCH 14/62] ignore PUT requests --- generator/JsonApiRequestGenerator.php | 17 ++++++++++++----- generator/JsonApiResourceGenerator.php | 16 ++++++++++++++-- src/Resource/Budget.php | 6 ------ src/Resource/Correction.php | 6 ------ src/Resource/Customer.php | 6 ------ src/Resource/Entry.php | 6 ------ src/Resource/Team.php | 6 ------ src/Resource/User.php | 6 ------ 8 files changed, 26 insertions(+), 43 deletions(-) diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 10575a0..4df30e7 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -13,13 +13,20 @@ class JsonApiRequestGenerator extends RequestGenerator { - protected function generateRequestClass(Endpoint $endpoint): PhpFile + public function generate($specification): array { - // Skip PUT requests - we only support POST (create) and PATCH (update) - if ($endpoint->method->isPut()) { - throw new \RuntimeException('PUT requests are not supported in Timatic. Use POST for create and PATCH for update.'); - } + // Filter out PUT endpoints before generating + $filteredSpec = clone $specification; + $filteredSpec->endpoints = array_filter( + $specification->endpoints, + fn ($endpoint) => ! $endpoint->method->isPut() + ); + + return parent::generate($filteredSpec); + } + protected function generateRequestClass(Endpoint $endpoint): PhpFile + { // Use parent generation for most of the class $phpFile = parent::generateRequestClass($endpoint); diff --git a/generator/JsonApiResourceGenerator.php b/generator/JsonApiResourceGenerator.php index b3ae414..c6743d2 100644 --- a/generator/JsonApiResourceGenerator.php +++ b/generator/JsonApiResourceGenerator.php @@ -10,6 +10,18 @@ class JsonApiResourceGenerator extends ResourceGenerator { + public function generate($specification): array + { + // Filter out PUT endpoints before generating + $filteredSpec = clone $specification; + $filteredSpec->endpoints = array_filter( + $specification->endpoints, + fn ($endpoint) => ! $endpoint->method->isPut() + ); + + return parent::generate($filteredSpec); + } + protected function generateResourceMethod(Endpoint $endpoint, Method $method): void { parent::generateResourceMethod($endpoint, $method); @@ -53,8 +65,8 @@ protected function addDataParameterToMethod(Endpoint $endpoint, Method $method): protected function isMutationRequest(Endpoint $endpoint): bool { + // Only POST and PATCH are supported (PUT is filtered out) return $endpoint->method->isPost() - || $endpoint->method->isPatch() - || $endpoint->method->isPut(); + || $endpoint->method->isPatch(); } } diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index d7a7b58..7504c91 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -9,7 +9,6 @@ use Timatic\SDK\Requests\Budget\GetBudgets; use Timatic\SDK\Requests\Budget\PatchBudget; use Timatic\SDK\Requests\Budget\PostBudgets; -use Timatic\SDK\Requests\Budget\PutBudget; class Budget extends BaseResource { @@ -48,11 +47,6 @@ public function getBudget(string $budget): Response return $this->connector->send(new GetBudget($budget)); } - public function putBudget(string $budget, \Timatic\SDK\Foundation\Model|array|null $data = null): Response - { - return $this->connector->send(new PutBudget($budget, $data)); - } - public function deleteBudget(string $budget): Response { return $this->connector->send(new DeleteBudget($budget)); diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index 4c93365..25c3543 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -6,7 +6,6 @@ use Saloon\Http\Response; use Timatic\SDK\Requests\Correction\PatchCorrection; use Timatic\SDK\Requests\Correction\PostCorrections; -use Timatic\SDK\Requests\Correction\PutCorrection; class Correction extends BaseResource { @@ -15,11 +14,6 @@ public function postCorrections(\Timatic\SDK\Foundation\Model|array|null $data = return $this->connector->send(new PostCorrections($data)); } - public function putCorrection(string $correction, \Timatic\SDK\Foundation\Model|array|null $data = null): Response - { - return $this->connector->send(new PutCorrection($correction, $data)); - } - public function patchCorrection(string $correction, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { return $this->connector->send(new PatchCorrection($correction, $data)); diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index 1c14e56..ceb571e 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -9,7 +9,6 @@ use Timatic\SDK\Requests\Customer\GetCustomers; use Timatic\SDK\Requests\Customer\PatchCustomer; use Timatic\SDK\Requests\Customer\PostCustomers; -use Timatic\SDK\Requests\Customer\PutCustomer; class Customer extends BaseResource { @@ -28,11 +27,6 @@ public function getCustomer(string $customer): Response return $this->connector->send(new GetCustomer($customer)); } - public function putCustomer(string $customer, \Timatic\SDK\Foundation\Model|array|null $data = null): Response - { - return $this->connector->send(new PutCustomer($customer, $data)); - } - public function deleteCustomer(string $customer): Response { return $this->connector->send(new DeleteCustomer($customer)); diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 7f8cd7c..9e7907a 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -9,7 +9,6 @@ use Timatic\SDK\Requests\Entry\GetEntry; use Timatic\SDK\Requests\Entry\PatchEntry; use Timatic\SDK\Requests\Entry\PostEntries; -use Timatic\SDK\Requests\Entry\PutEntry; class Entry extends BaseResource { @@ -96,11 +95,6 @@ public function getEntry(string $entry): Response return $this->connector->send(new GetEntry($entry)); } - public function putEntry(string $entry, \Timatic\SDK\Foundation\Model|array|null $data = null): Response - { - return $this->connector->send(new PutEntry($entry, $data)); - } - public function deleteEntry(string $entry): Response { return $this->connector->send(new DeleteEntry($entry)); diff --git a/src/Resource/Team.php b/src/Resource/Team.php index e79539a..256e25d 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -9,7 +9,6 @@ use Timatic\SDK\Requests\Team\GetTeams; use Timatic\SDK\Requests\Team\PatchTeam; use Timatic\SDK\Requests\Team\PostTeams; -use Timatic\SDK\Requests\Team\PutTeam; class Team extends BaseResource { @@ -28,11 +27,6 @@ public function getTeam(string $team): Response return $this->connector->send(new GetTeam($team)); } - public function putTeam(string $team, \Timatic\SDK\Foundation\Model|array|null $data = null): Response - { - return $this->connector->send(new PutTeam($team, $data)); - } - public function deleteTeam(string $team): Response { return $this->connector->send(new DeleteTeam($team)); diff --git a/src/Resource/User.php b/src/Resource/User.php index 2a03277..a0b3905 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -9,7 +9,6 @@ use Timatic\SDK\Requests\User\GetUsers; use Timatic\SDK\Requests\User\PatchUser; use Timatic\SDK\Requests\User\PostUsers; -use Timatic\SDK\Requests\User\PutUser; class User extends BaseResource { @@ -28,11 +27,6 @@ public function getUser(string $user): Response return $this->connector->send(new GetUser($user)); } - public function putUser(string $user, \Timatic\SDK\Foundation\Model|array|null $data = null): Response - { - return $this->connector->send(new PutUser($user, $data)); - } - public function deleteUser(string $user): Response { return $this->connector->send(new DeleteUser($user)); From ee11001acee115e52d7eaf9e2f31e5e226ce779e Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 20:57:55 +0100 Subject: [PATCH 15/62] add saloon-sdk-generator to composer require-dev --- .claude/settings.json | 14 ++++++++++++++ composer.json | 4 ++-- generator/generate.php | 4 +--- 3 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 .claude/settings.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..d5472b3 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,14 @@ +{ + "permissions": { + "allow": [], + "deny": [ + "Edit(file_path:src/Dto/**)", + "Edit(file_path:src/Requests/**)", + "Edit(file_path:src/Resource/**)", + "Write(file_path:src/Dto/**)", + "Write(file_path:src/Requests/**)", + "Write(file_path:src/Resource/**)" + ], + "ask": [] + } +} diff --git a/composer.json b/composer.json index ab48279..f0859a6 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,6 @@ "php": "^8.1", "saloonphp/saloon": "^3.0", "nesbot/carbon": "^2.0|^3.0", - "laravel/framework": "^10.0|^11.0|^12.0", "saloonphp/pagination-plugin": "^2.0" }, "require-dev": { @@ -27,7 +26,8 @@ "pestphp/pest": "^2.0|^3.0", "laravel/pint": "^1.0", "larastan/larastan": "^3.0", - "orchestra/testbench": "^10.0|^11.0|^12.0" + "orchestra/testbench": "^8.0|^9.0|^10.0", + "crescat-io/saloon-sdk-generator": "^1.4" }, "scripts": { "test": "pest", diff --git a/generator/generate.php b/generator/generate.php index d3dd987..f207e55 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -2,9 +2,7 @@ declare(strict_types=1); -// Use global composer autoload for SDK generator -require_once $_SERVER['HOME'].'/.composer/vendor/autoload.php'; -// Also load local vendor for our custom generators +// Use local vendor autoload require_once __DIR__.'/../vendor/autoload.php'; use Crescat\SaloonSdkGenerator\CodeGenerator; From afb3d5feb65c37183de9f2609685cc39ba0c7547 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 21:22:28 +0100 Subject: [PATCH 16/62] generate the connector --- .claude/settings.json | 2 + CLAUDE.md | 28 ++---- generator/JsonApiConnectorGenerator.php | 117 ++++++++++++++++++++++++ generator/generate.php | 2 + src/TimaticConnector.php | 10 +- 5 files changed, 136 insertions(+), 23 deletions(-) create mode 100644 generator/JsonApiConnectorGenerator.php diff --git a/.claude/settings.json b/.claude/settings.json index d5472b3..fe0be73 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -2,9 +2,11 @@ "permissions": { "allow": [], "deny": [ + "Edit(file_path:src/TimaticConnector.php)", "Edit(file_path:src/Dto/**)", "Edit(file_path:src/Requests/**)", "Edit(file_path:src/Resource/**)", + "Write(file_path:src/TimaticConnector.php)", "Write(file_path:src/Dto/**)", "Write(file_path:src/Requests/**)", "Write(file_path:src/Resource/**)" diff --git a/CLAUDE.md b/CLAUDE.md index d87d65c..da014c3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -276,18 +276,7 @@ PUT is intentionally excluded as PATCH provides better semantics for partial upd - **`src/Dto/`** - All Model/DTO classes (auto-generated with flattened JSON:API attributes) - **`src/Requests/`** - All Request classes (auto-generated from OpenAPI endpoints) - **`src/Resource/`** - All Resource classes (auto-generated to group related requests) - -### āš ļø SPECIAL CASE - Connector File: -- **`src/TimaticConnector.php`** - Partially auto-generated. The resource methods are auto-generated, but custom configuration methods MUST be preserved: - - `resolveBaseUrl()` - - `defaultHeaders()` - - `resolveResponseClass()` - - `paginate()` - - **After SDK regeneration, you MUST restore this file:** - ```bash - git checkout src/TimaticConnector.php - ``` +- **`src/TimaticConnector.php`** - The main Connector class (auto-generated with custom JSON:API configuration) ### āœ… SAFE TO EDIT: - **`src/Foundation/`** - Base classes (Model, HasAttributes, etc.) @@ -308,9 +297,6 @@ To update auto-generated files from the latest OpenAPI spec: # Run the regenerate script (handles all steps) composer regenerate -# IMPORTANT: Restore TimaticConnector (contains custom methods) -git checkout src/TimaticConnector.php - # Verify with tests composer test ``` @@ -320,8 +306,6 @@ composer test 2. Updates autoloader with `composer dump-autoload` 3. Formats all code with Laravel Pint -**Note:** You MUST run `git checkout src/TimaticConnector.php` after regeneration to restore custom configuration methods. - ## Modifying Auto-Generated Code If you need to change how files are generated, update the appropriate generator: @@ -330,12 +314,21 @@ If you need to change how files are generated, update the appropriate generator: | Directory | Generator | Description | |-----------|-----------|-------------| +| `src/TimaticConnector.php` | `generator/JsonApiConnectorGenerator.php` | Generates the main Connector with JSON:API configuration, pagination support, and custom headers. | | `src/Dto/` | `generator/JsonApiDtoGenerator.php` | Generates Models from OpenAPI schemas. Flattens JSON:API attributes into typed properties. Adds `#[Property]` and `#[DateTime]` attributes. | | `src/Requests/` | `generator/JsonApiRequestGenerator.php` | Generates Request classes. Adds Model support for POST/PUT/PATCH. Adds `Paginatable` interface to GET collections. | | `src/Resource/` | `generator/generate.php` (post-processing) | Resources are generated by SDK generator, then post-processed in generate.php to add `$data` parameters to POST/PUT/PATCH methods. | ### Generator Details: +- **`generator/JsonApiConnectorGenerator.php`** - Connector class generation + - Extends the base ConnectorGenerator from Saloon SDK Generator + - Adds `HasPagination` interface implementation + - Overrides `resolveBaseUrl()` to use Laravel config + - Adds `defaultHeaders()` with JSON:API headers and Bearer token support + - Adds `resolveResponseClass()` to return custom TimaticResponse + - Adds `paginate()` method with JsonApiPaginator support + - **`generator/JsonApiDtoGenerator.php`** - Model/DTO generation - Extends `Model` instead of Spatie Data - Flattens JSON:API `attributes` object into direct properties @@ -356,7 +349,6 @@ If you need to change how files are generated, update the appropriate generator: After modifying generators, regenerate and test: ```bash php generator/generate.php -git checkout src/TimaticConnector.php composer dump-autoload ./vendor/bin/pest ./vendor/bin/pint diff --git a/generator/JsonApiConnectorGenerator.php b/generator/JsonApiConnectorGenerator.php new file mode 100644 index 0000000..0f0b31c --- /dev/null +++ b/generator/JsonApiConnectorGenerator.php @@ -0,0 +1,117 @@ +getNamespaces())[0]; + /** @var ClassType $classType */ + $classType = array_values($namespace->getClasses())[0]; + + // Add HasPagination interface + $namespace->addUse(HasPagination::class); + $classType->addImplement(HasPagination::class); + + // Add additional imports for custom methods + $namespace->addUse(Request::class); + $namespace->addUse(JsonApiPaginator::class); + $namespace->addUse(TimaticResponse::class); + + // Remove empty constructor if present + $this->removeEmptyConstructorIfPresent($classType); + + // Override resolveBaseUrl to use Laravel config + $resolveBaseUrl = $classType->getMethod('resolveBaseUrl'); + $resolveBaseUrl->setBody('return config(\'timatic.base_url\');'); + + // Store all resource methods (to re-add them later in correct order) + $resourceMethods = []; + foreach ($classType->getMethods() as $methodName => $method) { + $resourceMethods[$methodName] = $method; + $classType->removeMethod($methodName); + } + + // Add defaultHeaders method (after resolveBaseUrl) + $this->addDefaultHeaders($classType); + + // Add resolveResponseClass method + $this->addResolveResponseClassMethod($classType); + + // Add paginate method + $this->addPaginateMethod($classType); + + // Re-add resource methods after custom configuration methods + foreach ($resourceMethods as $methodName => $method) { + $classType->setMethods(array_merge($classType->getMethods(), [$methodName => $method])); + } + + return $phpFile; + } + + public function removeEmptyConstructorIfPresent(ClassType $classType): void + { + if ($classType->hasMethod('__construct')) { + $constructor = $classType->getMethod('__construct'); + // Only remove if it's empty (no parameters and no body) + if (count($constructor->getParameters()) === 0 && empty(trim($constructor->getBody()))) { + $classType->removeMethod('__construct'); + } + } + } + + public function addDefaultHeaders(ClassType $classType): void + { + $defaultHeaders = $classType->addMethod('defaultHeaders') + ->setProtected() + ->setReturnType('array'); + + $defaultHeaders->addBody('$headers = ['); + $defaultHeaders->addBody(' \'Accept\' => \'application/vnd.api+json\','); + $defaultHeaders->addBody(' \'Content-Type\' => \'application/vnd.api+json\','); + $defaultHeaders->addBody('];'); + $defaultHeaders->addBody(''); + $defaultHeaders->addBody('if ($token = config(\'timatic.api_token\')) {'); + $defaultHeaders->addBody(' $headers[\'Authorization\'] = "Bearer {$token}";'); + $defaultHeaders->addBody('}'); + $defaultHeaders->addBody(''); + $defaultHeaders->addBody('return $headers;'); + } + + public function addPaginateMethod(ClassType $classType): void + { + $paginate = $classType->addMethod('paginate') + ->setPublic() + ->setReturnType(JsonApiPaginator::class); + + $paginate->addParameter('request') + ->setType(Request::class); + + $paginate->setBody('return new ?($this, $request);', [new Literal('JsonApiPaginator')]); + } + + public function addResolveResponseClassMethod(ClassType $classType): void + { + $classType->addMethod('resolveResponseClass') + ->setPublic() + ->setReturnType('string') + ->setBody('return ?;', [new Literal('TimaticResponse::class')]); + } +} diff --git a/generator/generate.php b/generator/generate.php index f207e55..147b870 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -8,6 +8,7 @@ use Crescat\SaloonSdkGenerator\CodeGenerator; use Crescat\SaloonSdkGenerator\Data\Generator\Config; use Crescat\SaloonSdkGenerator\Factory; +use Timatic\SDK\Generator\JsonApiConnectorGenerator; use Timatic\SDK\Generator\JsonApiDtoGenerator; use Timatic\SDK\Generator\JsonApiRequestGenerator; use Timatic\SDK\Generator\JsonApiResourceGenerator; @@ -74,6 +75,7 @@ echo "šŸ—ļø Generating SDK with JSON:API models...\n"; $generator = new CodeGenerator( config: $config, + connectorGenerator: new JsonApiConnectorGenerator($config), dtoGenerator: new JsonApiDtoGenerator($config), requestGenerator: new JsonApiRequestGenerator($config), resourceGenerator: new JsonApiResourceGenerator($config) diff --git a/src/TimaticConnector.php b/src/TimaticConnector.php index d290430..a5408ad 100644 --- a/src/TimaticConnector.php +++ b/src/TimaticConnector.php @@ -37,11 +37,6 @@ */ class TimaticConnector extends Connector implements HasPagination { - public function resolveBaseUrl(): string - { - return config('timatic.base_url'); - } - protected function defaultHeaders(): array { $headers = [ @@ -66,6 +61,11 @@ public function paginate(Request $request): JsonApiPaginator return new JsonApiPaginator($this, $request); } + public function resolveBaseUrl(): string + { + return config('timatic.base_url'); + } + public function approve(): Approve { return new Approve($this); From 8c32bc8c3abb9e76152393b13c2b3657746b2b44 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 22:23:37 +0100 Subject: [PATCH 17/62] wip automated tests --- .claude/settings.json | 13 +- CLAUDE.md | 25 +- generator/JsonApiConnectorGenerator.php | 4 +- generator/JsonApiPestTestGenerator.php | 165 ++++---------- generator/JsonApiRequestGenerator.php | 9 +- generator/generate.php | 64 +++++- generator/stubs/pest-resource-test-func.stub | 11 + generator/stubs/pest-resource-test.stub | 11 + .../Approve/PostOvertimeApproveRequest.php | 4 +- src/Requests/Budget/PatchBudgetRequest.php | 4 +- src/Requests/Budget/PostBudgetsRequest.php | 4 +- .../Correction/PatchCorrectionRequest.php | 4 +- .../Correction/PostCorrectionsRequest.php | 4 +- .../Customer/PatchCustomerRequest.php | 4 +- .../Customer/PostCustomersRequest.php | 4 +- src/Requests/Entry/PatchEntryRequest.php | 4 +- src/Requests/Entry/PostEntriesRequest.php | 4 +- src/Requests/Event/PostEventsRequest.php | 4 +- .../PostOvertimeMarkAsExportedRequest.php | 4 +- .../PostEntryMarkAsInvoicedRequest.php | 4 +- src/Requests/Team/PatchTeamRequest.php | 4 +- src/Requests/Team/PostTeamsRequest.php | 4 +- src/Requests/User/PatchUserRequest.php | 4 +- src/Requests/User/PostUsersRequest.php | 4 +- src/Resource/Approve.php | 4 +- src/Resource/Budget.php | 20 +- src/Resource/BudgetTimeSpentTotal.php | 4 +- src/Resource/BudgetType.php | 4 +- src/Resource/Change.php | 8 +- src/Resource/Correction.php | 8 +- src/Resource/Customer.php | 20 +- src/Resource/DailyProgress.php | 4 +- src/Resource/EntriesExport.php | 4 +- src/Resource/Entry.php | 20 +- src/Resource/EntrySuggestion.php | 12 +- src/Resource/Event.php | 4 +- src/Resource/ExportMail.php | 4 +- src/Resource/Incident.php | 8 +- src/Resource/MarkAsExported.php | 4 +- src/Resource/MarkAsInvoiced.php | 4 +- src/Resource/Me.php | 4 +- src/Resource/Number.php | 4 +- src/Resource/Overtime.php | 4 +- src/Resource/Period.php | 4 +- src/Resource/Team.php | 20 +- src/Resource/TimeSpentTotal.php | 4 +- src/Resource/User.php | 20 +- src/Resource/UserCustomerHoursAggregate.php | 4 +- src/TimaticConnector.php | 2 + tests/ApproveTest.php | 25 ++ tests/BudgetTest.php | 104 +++++++++ tests/BudgetTimeSpentTotalTest.php | 26 +++ tests/BudgetTypeTest.php | 25 ++ tests/ChangeTest.php | 40 ++++ tests/CorrectionTest.php | 40 ++++ tests/CustomerTest.php | 86 +++++++ tests/DailyProgressTest.php | 25 ++ tests/EntriesExportTest.php | 93 ++++++++ tests/EntrySuggestionTest.php | 62 +++++ tests/EntryTest.php | 152 +++++++++++++ tests/EventTest.php | 25 ++ tests/ExportMailTest.php | 25 ++ .../Saloon/approve.postOvertimeApprove.json | 20 ++ .../Fixtures/Saloon/budget.deleteBudget.json | 20 ++ tests/Fixtures/Saloon/budget.getBudget.json | 20 ++ tests/Fixtures/Saloon/budget.getBudgets.json | 20 ++ tests/Fixtures/Saloon/budget.patchBudget.json | 20 ++ tests/Fixtures/Saloon/budget.postBudgets.json | 20 ++ ...meSpentTotal.getBudgetTimeSpentTotals.json | 20 ++ .../Saloon/budgetType.getBudgetTypes.json | 20 ++ tests/Fixtures/Saloon/change.getChange.json | 20 ++ tests/Fixtures/Saloon/change.getChanges.json | 20 ++ .../Saloon/correction.patchCorrection.json | 20 ++ .../Saloon/correction.postCorrections.json | 20 ++ .../Saloon/customer.deleteCustomer.json | 20 ++ .../Fixtures/Saloon/customer.getCustomer.json | 20 ++ .../Saloon/customer.getCustomers.json | 20 ++ .../Saloon/customer.patchCustomer.json | 20 ++ .../Saloon/customer.postCustomers.json | 20 ++ .../dailyProgress.getDailyProgresses.json | 20 ++ .../entriesExport.getBudgetEntriesExport.json | 20 ++ tests/Fixtures/Saloon/entry.deleteEntry.json | 20 ++ tests/Fixtures/Saloon/entry.getEntries.json | 20 ++ tests/Fixtures/Saloon/entry.getEntry.json | 20 ++ tests/Fixtures/Saloon/entry.patchEntry.json | 20 ++ tests/Fixtures/Saloon/entry.postEntries.json | 20 ++ ...entrySuggestion.deleteEntrySuggestion.json | 20 ++ .../entrySuggestion.getEntrySuggestion.json | 20 ++ .../entrySuggestion.getEntrySuggestions.json | 20 ++ tests/Fixtures/Saloon/event.postEvents.json | 20 ++ .../exportMail.getBudgetsExportMails.json | 20 ++ .../Fixtures/Saloon/incident.getIncident.json | 20 ++ .../Saloon/incident.getIncidents.json | 20 ++ ...AsExported.postOvertimeMarkAsExported.json | 20 ++ ...arkAsInvoiced.postEntryMarkAsInvoiced.json | 20 ++ tests/Fixtures/Saloon/me.getMes.json | 20 ++ .../Saloon/number.getIncidentsNumber.json | 20 ++ .../Saloon/overtime.getOvertimes.json | 20 ++ .../Saloon/period.getBudgetPeriods.json | 20 ++ tests/Fixtures/Saloon/team.deleteTeam.json | 20 ++ tests/Fixtures/Saloon/team.getTeam.json | 20 ++ tests/Fixtures/Saloon/team.getTeams.json | 20 ++ tests/Fixtures/Saloon/team.patchTeam.json | 20 ++ tests/Fixtures/Saloon/team.postTeams.json | 20 ++ .../timeSpentTotal.getTimeSpentTotals.json | 20 ++ tests/Fixtures/Saloon/user.deleteUser.json | 20 ++ tests/Fixtures/Saloon/user.getUser.json | 20 ++ tests/Fixtures/Saloon/user.getUsers.json | 20 ++ tests/Fixtures/Saloon/user.patchUser.json | 20 ++ tests/Fixtures/Saloon/user.postUsers.json | 20 ++ ...regate.getUserCustomerHoursAggregates.json | 20 ++ tests/IncidentTest.php | 40 ++++ tests/JsonApiBodyTest.php | 79 ------- tests/MarkAsExportedTest.php | 25 ++ tests/MarkAsInvoicedTest.php | 25 ++ tests/MeTest.php | 25 ++ tests/NumberTest.php | 25 ++ tests/OvertimeTest.php | 50 ++++ tests/PaginationTest.php | 112 --------- tests/PeriodTest.php | 25 ++ tests/Pest.php | 19 +- tests/TeamTest.php | 85 +++++++ tests/TestCase.php | 31 +++ tests/TimaticResponseTest.php | 215 ------------------ tests/TimeSpentTotalTest.php | 30 +++ tests/UserCustomerHoursAggregateTest.php | 56 +++++ tests/UserTest.php | 86 +++++++ 127 files changed, 2491 insertions(+), 709 deletions(-) create mode 100644 generator/stubs/pest-resource-test-func.stub create mode 100644 generator/stubs/pest-resource-test.stub create mode 100644 tests/ApproveTest.php create mode 100644 tests/BudgetTest.php create mode 100644 tests/BudgetTimeSpentTotalTest.php create mode 100644 tests/BudgetTypeTest.php create mode 100644 tests/ChangeTest.php create mode 100644 tests/CorrectionTest.php create mode 100644 tests/CustomerTest.php create mode 100644 tests/DailyProgressTest.php create mode 100644 tests/EntriesExportTest.php create mode 100644 tests/EntrySuggestionTest.php create mode 100644 tests/EntryTest.php create mode 100644 tests/EventTest.php create mode 100644 tests/ExportMailTest.php create mode 100644 tests/Fixtures/Saloon/approve.postOvertimeApprove.json create mode 100644 tests/Fixtures/Saloon/budget.deleteBudget.json create mode 100644 tests/Fixtures/Saloon/budget.getBudget.json create mode 100644 tests/Fixtures/Saloon/budget.getBudgets.json create mode 100644 tests/Fixtures/Saloon/budget.patchBudget.json create mode 100644 tests/Fixtures/Saloon/budget.postBudgets.json create mode 100644 tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json create mode 100644 tests/Fixtures/Saloon/budgetType.getBudgetTypes.json create mode 100644 tests/Fixtures/Saloon/change.getChange.json create mode 100644 tests/Fixtures/Saloon/change.getChanges.json create mode 100644 tests/Fixtures/Saloon/correction.patchCorrection.json create mode 100644 tests/Fixtures/Saloon/correction.postCorrections.json create mode 100644 tests/Fixtures/Saloon/customer.deleteCustomer.json create mode 100644 tests/Fixtures/Saloon/customer.getCustomer.json create mode 100644 tests/Fixtures/Saloon/customer.getCustomers.json create mode 100644 tests/Fixtures/Saloon/customer.patchCustomer.json create mode 100644 tests/Fixtures/Saloon/customer.postCustomers.json create mode 100644 tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json create mode 100644 tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json create mode 100644 tests/Fixtures/Saloon/entry.deleteEntry.json create mode 100644 tests/Fixtures/Saloon/entry.getEntries.json create mode 100644 tests/Fixtures/Saloon/entry.getEntry.json create mode 100644 tests/Fixtures/Saloon/entry.patchEntry.json create mode 100644 tests/Fixtures/Saloon/entry.postEntries.json create mode 100644 tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json create mode 100644 tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json create mode 100644 tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json create mode 100644 tests/Fixtures/Saloon/event.postEvents.json create mode 100644 tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json create mode 100644 tests/Fixtures/Saloon/incident.getIncident.json create mode 100644 tests/Fixtures/Saloon/incident.getIncidents.json create mode 100644 tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json create mode 100644 tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json create mode 100644 tests/Fixtures/Saloon/me.getMes.json create mode 100644 tests/Fixtures/Saloon/number.getIncidentsNumber.json create mode 100644 tests/Fixtures/Saloon/overtime.getOvertimes.json create mode 100644 tests/Fixtures/Saloon/period.getBudgetPeriods.json create mode 100644 tests/Fixtures/Saloon/team.deleteTeam.json create mode 100644 tests/Fixtures/Saloon/team.getTeam.json create mode 100644 tests/Fixtures/Saloon/team.getTeams.json create mode 100644 tests/Fixtures/Saloon/team.patchTeam.json create mode 100644 tests/Fixtures/Saloon/team.postTeams.json create mode 100644 tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json create mode 100644 tests/Fixtures/Saloon/user.deleteUser.json create mode 100644 tests/Fixtures/Saloon/user.getUser.json create mode 100644 tests/Fixtures/Saloon/user.getUsers.json create mode 100644 tests/Fixtures/Saloon/user.patchUser.json create mode 100644 tests/Fixtures/Saloon/user.postUsers.json create mode 100644 tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json create mode 100644 tests/IncidentTest.php delete mode 100644 tests/JsonApiBodyTest.php create mode 100644 tests/MarkAsExportedTest.php create mode 100644 tests/MarkAsInvoicedTest.php create mode 100644 tests/MeTest.php create mode 100644 tests/NumberTest.php create mode 100644 tests/OvertimeTest.php delete mode 100644 tests/PaginationTest.php create mode 100644 tests/PeriodTest.php create mode 100644 tests/TeamTest.php create mode 100644 tests/TestCase.php delete mode 100644 tests/TimaticResponseTest.php create mode 100644 tests/TimeSpentTotalTest.php create mode 100644 tests/UserCustomerHoursAggregateTest.php create mode 100644 tests/UserTest.php diff --git a/.claude/settings.json b/.claude/settings.json index fe0be73..0107af1 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,15 +1,24 @@ { "permissions": { - "allow": [], + "allow": [ + "Edit(file_path:tests/Pest.php)", + "Edit(file_path:tests/TestCase.php)", + "Edit(file_path:generator/**)", + "Write(file_path:tests/Pest.php)", + "Write(file_path:tests/TestCase.php)", + "Write(file_path:generator/**)" + ], "deny": [ "Edit(file_path:src/TimaticConnector.php)", "Edit(file_path:src/Dto/**)", "Edit(file_path:src/Requests/**)", "Edit(file_path:src/Resource/**)", + "Edit(file_path:tests/*Test.php)", "Write(file_path:src/TimaticConnector.php)", "Write(file_path:src/Dto/**)", "Write(file_path:src/Requests/**)", - "Write(file_path:src/Resource/**)" + "Write(file_path:src/Resource/**)", + "Write(file_path:tests/*Test.php)" ], "ask": [] } diff --git a/CLAUDE.md b/CLAUDE.md index da014c3..d576430 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -272,11 +272,12 @@ This SDK **does not support PUT requests**. Only the following HTTP methods are PUT is intentionally excluded as PATCH provides better semantics for partial updates in JSON:API applications. The generator will throw an exception if it encounters PUT endpoints. -### āŒ NEVER EDIT THESE DIRECTORIES: +### āŒ NEVER EDIT THESE FILES/DIRECTORIES: - **`src/Dto/`** - All Model/DTO classes (auto-generated with flattened JSON:API attributes) - **`src/Requests/`** - All Request classes (auto-generated from OpenAPI endpoints) - **`src/Resource/`** - All Resource classes (auto-generated to group related requests) - **`src/TimaticConnector.php`** - The main Connector class (auto-generated with custom JSON:API configuration) +- **`tests/*Test.php`** - All test files (auto-generated from OpenAPI endpoints, Pest.php and TestCase.php are preserved) ### āœ… SAFE TO EDIT: - **`src/Foundation/`** - Base classes (Model, HasAttributes, etc.) @@ -286,11 +287,15 @@ PUT is intentionally excluded as PATCH provides better semantics for partial upd - **`src/Providers/`** - Laravel service providers - **`src/Facades/`** - Laravel facades - **`config/timatic.php`** - Configuration file -- **`tests/`** - Test files -- **`generator/`** - Generator classes +- **`tests/Pest.php`** - Pest configuration (preserved during regeneration) +- **`tests/TestCase.php`** - Test base class with Orchestra Testbench setup (preserved during regeneration) +- **`generator/`** - Generator classes that customize SDK generation +- **`generator/Stubs/`** - Custom Pest test stub templates for JSON:API ## SDK Regeneration Commands +**IMPORTANT:** Always use `composer regenerate` to regenerate the SDK. Never run `php generator/generate.php` directly. + To update auto-generated files from the latest OpenAPI spec: ```bash @@ -302,9 +307,11 @@ composer test ``` **What `composer regenerate` does:** -1. Runs `php generator/generate.php` to download OpenAPI spec and generate files -2. Updates autoloader with `composer dump-autoload` -3. Formats all code with Laravel Pint +1. Downloads OpenAPI spec from the API +2. Generates all source files (Connector, DTOs, Requests, Resources) +3. Generates Pest test files for all endpoints +4. Updates autoloader with `composer dump-autoload` +5. Formats all code with Laravel Pint ## Modifying Auto-Generated Code @@ -348,8 +355,6 @@ If you need to change how files are generated, update the appropriate generator: After modifying generators, regenerate and test: ```bash -php generator/generate.php -composer dump-autoload -./vendor/bin/pest -./vendor/bin/pint +composer regenerate +composer test ``` diff --git a/generator/JsonApiConnectorGenerator.php b/generator/JsonApiConnectorGenerator.php index 0f0b31c..c2941d3 100644 --- a/generator/JsonApiConnectorGenerator.php +++ b/generator/JsonApiConnectorGenerator.php @@ -35,8 +35,8 @@ protected function generateConnectorClass(ApiSpecification $specification): ?Php $namespace->addUse(JsonApiPaginator::class); $namespace->addUse(TimaticResponse::class); - // Remove empty constructor if present - $this->removeEmptyConstructorIfPresent($classType); + // Keep the empty constructor for test compatibility + // (PestTestGenerator needs it) // Override resolveBaseUrl to use Laravel config $resolveBaseUrl = $classType->getMethod('resolveBaseUrl'); diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index aa350d0..322f096 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -5,149 +5,62 @@ namespace Timatic\SDK\Generator; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; -use Crescat\SaloonSdkGenerator\Data\TaggedOutputFile; use Crescat\SaloonSdkGenerator\Generators\PestTestGenerator; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use Exception; -use Illuminate\Support\Arr; -use Illuminate\Support\Str; -use Nette\PhpGenerator\PhpFile; class JsonApiPestTestGenerator extends PestTestGenerator { /** - * @param array|Endpoint[] $endpoints + * Skip generating Pest.php - we have a custom version */ - public function generateTest(string $resourceName, array $endpoints): PhpFile|TaggedOutputFile|null + protected function shouldGeneratePestFile(): bool { - $fileStub = file_get_contents(__DIR__.'/../../../saloon-sdk-generator-jsonapi/src/Stubs/pest-resource-test.stub'); - - $fileStub = str_replace('{{ prelude }}', '// Generated '.date('Y-m-d H:i:s'), $fileStub); - $fileStub = str_replace('{{ connectorName }}', $this->config->connectorName, $fileStub); - $fileStub = str_replace('{{ namespace }}', $this->config->namespace, $fileStub); - $fileStub = str_replace('{{ name }}', $this->config->connectorName, $fileStub); - $fileStub = str_replace('{{ clientName }}', NameHelper::safeVariableName($this->config->connectorName), $fileStub); - - $namespace = Arr::first($this->generatedCode->connectorClass->getNamespaces()); - $classType = Arr::first($namespace->getClasses()); - - $constructorParameters = $classType->getMethod('__construct')->getParameters(); - $constructorArgs = []; - - foreach ($constructorParameters as $parameter) { - if ($parameter->isNullable()) { - continue; - } - - $defaultValue = match ($parameter->getType()) { - 'string' => "'replace'", - 'bool' => 'true', - 'int' => 0, - default => 'null', - }; - - $constructorArgs[] = $parameter->getName().': '.$defaultValue; - } - - $fileStub = str_replace('{{ connectorArgs }}', Str::wrap(implode(",\n\t\t", $constructorArgs), "\n\t\t", "\n\t"), $fileStub); - - // Generate imports - $imports = []; - foreach ($endpoints as $endpoint) { - $requestClassName = NameHelper::resourceClassName($endpoint->name); - $imports[] = "use {$this->config->namespace}\\{$this->config->requestNamespaceSuffix}\\{$resourceName}\\{$requestClassName};"; - } - - // Add Saloon testing imports - $imports[] = 'use Saloon\\Http\\Faking\\MockClient;'; - $imports[] = 'use Saloon\\Http\\Faking\\MockResponse;'; - $imports[] = 'use Saloon\\Http\\Request;'; - - $fileStub = str_replace('{{ requestImports }}', implode("\n", $imports), $fileStub); - - // Generate DTO imports - $dtoImports = $this->generateDtoImports($endpoints); - $fileStub = str_replace('{{ dtoImports }}', implode("\n", $dtoImports), $fileStub); - - // Generate test functions - foreach ($endpoints as $endpoint) { - $requestClassName = NameHelper::resourceClassName($endpoint->name); - $requestClassNameAlias = $requestClassName == $resourceName ? "{$requestClassName}Request" : null; - - $testFunction = $this->generateTestFunction($endpoint, $resourceName, $requestClassName, $requestClassNameAlias); - $fileStub .= "\n\n{$testFunction}"; - } - - try { - return new TaggedOutputFile( - tag: 'pest', - file: $fileStub, - path: "tests/{$resourceName}Test.php", - ); - } catch (Exception $e) { - return null; - } + return false; } - protected function generateTestFunction(Endpoint $endpoint, string $resourceName, string $requestClassName, ?string $requestClassNameAlias): string + /** + * Skip generating TestCase.php - we have a custom version + */ + protected function shouldGenerateTestCaseFile(): bool { - $isMutation = $endpoint->method->isPost() || $endpoint->method->isPatch() || $endpoint->method->isPut(); - - // Use JSON:API stub for mutations, regular stub for others - $stubPath = $isMutation - ? __DIR__.'/stubs/pest-jsonapi-test-func.stub' - : __DIR__.'/../../../saloon-sdk-generator-jsonapi/src/Stubs/pest-resource-test-func.stub'; - - $functionStub = file_get_contents($stubPath); - - $clientName = NameHelper::safeVariableName($this->config->connectorName); - $resourceNameSafe = NameHelper::safeVariableName($resourceName); - $methodNameSafe = NameHelper::safeVariableName($requestClassName); - - $functionStub = str_replace('{{ clientName }}', $clientName, $functionStub); - $functionStub = str_replace('{{ requestClass }}', $requestClassNameAlias ?? $requestClassName, $functionStub); - $functionStub = str_replace('{{ resourceName }}', $resourceNameSafe, $functionStub); - $functionStub = str_replace('{{ methodName }}', $methodNameSafe, $functionStub); - $functionStub = str_replace('{{ fixtureName }}', Str::camel($resourceNameSafe.'.'.$methodNameSafe), $functionStub); - - $description = $isMutation - ? "sends valid JSON:API body when calling {$methodNameSafe} in {$resourceName}" - : "calls the {$methodNameSafe} method in the {$resourceName} resource"; - - $functionStub = str_replace('{{ testDescription }}', $description, $functionStub); + return false; + } - // Set response status - $responseStatus = $endpoint->method->isPost() ? 201 : 200; - $functionStub = str_replace('{{ responseStatus }}', (string) $responseStatus, $functionStub); + /** + * Filter out PUT requests - not supported in JSON:API + */ + protected function shouldIncludeEndpoint(Endpoint $endpoint): bool + { + return ! $endpoint->method->isPut(); + } - if ($isMutation) { - // For mutations, create a Model instance - $dtoClassName = NameHelper::dtoClassName($resourceName); - $modelSetup = " \${$resourceNameSafe} = new {$dtoClassName}(['title' => 'Test {$resourceName}']);\n"; - $methodArguments = "\${$resourceNameSafe}"; + /** + * Use our custom JSON:API test stub + */ + protected function getTestStubPath(): string + { + return __DIR__.'/Stubs/pest-resource-test.stub'; + } - // Add simple body assertion - $bodyAssertions = " // Validate attributes exist\n"; - $bodyAssertions .= " expect(\$data['attributes'])->toBeArray();\n"; + /** + * Use our custom JSON:API test function stub + */ + protected function getTestFunctionStubPath(): string + { + return __DIR__.'/Stubs/pest-resource-test-func.stub'; + } - $functionStub = str_replace('{{ modelSetup }}', $modelSetup, $functionStub); - $functionStub = str_replace('{{ methodArguments }}', $methodArguments, $functionStub); - $functionStub = str_replace('{{ bodyAssertions }}', $bodyAssertions, $functionStub); - } else { - // For non-mutations, use regular fixture-based approach - $methodArguments = []; - foreach ($endpoint->allParameters() as $param) { - $methodArguments[] = sprintf('%s: %s', NameHelper::safeVariableName($param->name), match ($param->type) { - 'string' => "'test string'", - 'int', 'integer' => '123', - default => 'null', - }); - } + /** + * Add "Request" suffix to match JsonApiRequestGenerator behavior + */ + protected function getRequestClassName(Endpoint $endpoint): string + { + $className = NameHelper::requestClassName($endpoint->name ?: NameHelper::pathBasedName($endpoint)); - $methodArguments = Str::wrap(implode(",\n\t\t", $methodArguments), "\n\t\t", "\n\t"); - $functionStub = str_replace('{{ methodArguments }}', $methodArguments, $functionStub); + if (! str_ends_with($className, 'Request')) { + $className .= 'Request'; } - return $functionStub; + return $className; } } diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 4df30e7..b11e98a 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -81,15 +81,16 @@ protected function addModelDataParameter(ClassType $classType, $namespace): void // Get constructor $constructor = $classType->getMethod('__construct'); - // Add data parameter with Model-only type (no array support) + // Add data parameter with Model|array|null type to match Resource signature $constructor->addPromotedParameter('data') - ->setType('\\Timatic\\SDK\\Foundation\\Model') - ->setProtected(); + ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') + ->setProtected() + ->setNullable(); // Add defaultBody method $defaultBody = $classType->addMethod('defaultBody') ->setProtected() ->setReturnType('array') - ->addBody('return $this->data->toJsonApi();'); + ->addBody('return $this->data ? $this->data->toJsonApi() : [];'); } } diff --git a/generator/generate.php b/generator/generate.php index 147b870..1066685 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -10,6 +10,7 @@ use Crescat\SaloonSdkGenerator\Factory; use Timatic\SDK\Generator\JsonApiConnectorGenerator; use Timatic\SDK\Generator\JsonApiDtoGenerator; +use Timatic\SDK\Generator\JsonApiPestTestGenerator; use Timatic\SDK\Generator\JsonApiRequestGenerator; use Timatic\SDK\Generator\JsonApiResourceGenerator; @@ -55,6 +56,17 @@ echo ' āœ“ Removed '.basename($folder)."\n"; } } + +// Clean up generated test files (but keep Pest.php and TestCase.php) +$testsFolder = __DIR__.'/../tests'; +if (is_dir($testsFolder)) { + $testFiles = glob($testsFolder.'/*Test.php'); + foreach ($testFiles as $testFile) { + unlink($testFile); + } + echo ' āœ“ Removed generated test files'."\n"; +} + echo "āœ… Cleanup completed\n\n"; // Parse the specification @@ -78,15 +90,15 @@ connectorGenerator: new JsonApiConnectorGenerator($config), dtoGenerator: new JsonApiDtoGenerator($config), requestGenerator: new JsonApiRequestGenerator($config), - resourceGenerator: new JsonApiResourceGenerator($config) + resourceGenerator: new JsonApiResourceGenerator($config), + postProcessors: [new JsonApiPestTestGenerator] ); // Generate the code $result = $generator->run($specification); -// Tests are not generated in this version (requires SDK generator v1.4+) -// You can manually add Pest tests for JSON:API validation -$tests = null; +// Extract tests from result +$tests = $result->additionalFiles ?? null; // Output directory $outputDir = __DIR__.'/../src'; @@ -118,12 +130,42 @@ function writeFile($file, $outputDir, $namespace) // Post-process and write resources echo "\nšŸ“¦ Resources:\n"; foreach ($result->resourceClasses as $resourceClass) { - // Fix POST/PUT/PATCH methods to add $data parameter $namespace = array_values($resourceClass->getNamespaces())[0]; $classType = array_values($namespace->getClasses())[0]; + // Fix imports - add "Request" suffix to all Request class imports + foreach ($namespace->getUses() as $alias => $fqn) { + // Check if this is a Request class import + if (str_contains($fqn, '\\Requests\\')) { + $className = basename(str_replace('\\', '/', $fqn)); + // Add "Request" suffix if not already present + if (! str_ends_with($className, 'Request')) { + $newFqn = substr($fqn, 0, -strlen($className)).$className.'Request'; + $namespace->removeUse($fqn); + $namespace->addUse($newFqn, $className.'Request'); + } + } + } + + // Fix POST/PUT/PATCH methods to add $data parameter and update class references foreach ($classType->getMethods() as $method) { $methodName = $method->getName(); + $body = $method->getBody(); + + // Add "Request" suffix to class instantiations in method body + $body = preg_replace_callback( + '/\(new\s+(\w+)\(/', + function ($matches) { + $className = $matches[1]; + // Add "Request" suffix if not already present + if (! str_ends_with($className, 'Request')) { + return '(new '.$className.'Request('; + } + + return $matches[0]; + }, + $body + ); // Check if it's a mutation method (post/patch only, PUT is not supported) if (preg_match('/^(post|patch)/i', $methodName)) { @@ -133,27 +175,25 @@ function writeFile($file, $outputDir, $namespace) ->setDefaultValue(null); // Update method body to pass $data to request constructor - $body = $method->getBody(); - // Pattern 2 first: new Request($param) -> new Request($param, $data) // Must be checked before Pattern 1 to avoid double replacement - if (preg_match('/\(new\s+\w+\([^)]+\)\)/', $body)) { + if (preg_match('/\(new\s+\w+Request\([^)]+\)\)/', $body)) { $body = preg_replace( - '/\(new\s+(\w+)\(([^)]+)\)\)/', + '/\(new\s+(\w+Request)\(([^)]+)\)\)/', '(new $1($2, $data))', $body ); } else { // Pattern 1: new Request() -> new Request($data) $body = preg_replace( - '/\(new\s+(\w+)\(\)\)/', + '/\(new\s+(\w+Request)\(\)\)/', '(new $1($data))', $body ); } - - $method->setBody($body); } + + $method->setBody($body); } $path = writeFile($resourceClass, $outputDir, $config->namespace); diff --git a/generator/stubs/pest-resource-test-func.stub b/generator/stubs/pest-resource-test-func.stub new file mode 100644 index 0000000..d534dae --- /dev/null +++ b/generator/stubs/pest-resource-test-func.stub @@ -0,0 +1,11 @@ +it('{{ testDescription }}', function () { + Saloon::fake([ + {{ requestClass }}::class => MockResponse::fixture('{{ fixtureName }}'), + ]); + + $response = $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ methodArguments }}); + + Saloon::assertSent({{ requestClass }}::class); + + expect($response->status())->toBe(200); +}); diff --git a/generator/stubs/pest-resource-test.stub b/generator/stubs/pest-resource-test.stub new file mode 100644 index 0000000..de66ce9 --- /dev/null +++ b/generator/stubs/pest-resource-test.stub @@ -0,0 +1,11 @@ +{{ clientName }} = new {{ namespace }}\{{ connectorName }}({{ connectorArgs }}); +}); diff --git a/src/Requests/Approve/PostOvertimeApproveRequest.php b/src/Requests/Approve/PostOvertimeApproveRequest.php index 32eda71..ae73914 100644 --- a/src/Requests/Approve/PostOvertimeApproveRequest.php +++ b/src/Requests/Approve/PostOvertimeApproveRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $overtime, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Budget/PatchBudgetRequest.php b/src/Requests/Budget/PatchBudgetRequest.php index bef29f8..5eae0de 100644 --- a/src/Requests/Budget/PatchBudgetRequest.php +++ b/src/Requests/Budget/PatchBudgetRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $budget, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Budget/PostBudgetsRequest.php b/src/Requests/Budget/PostBudgetsRequest.php index ffd7569..f553f9b 100644 --- a/src/Requests/Budget/PostBudgetsRequest.php +++ b/src/Requests/Budget/PostBudgetsRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Correction/PatchCorrectionRequest.php b/src/Requests/Correction/PatchCorrectionRequest.php index f234b54..394f778 100644 --- a/src/Requests/Correction/PatchCorrectionRequest.php +++ b/src/Requests/Correction/PatchCorrectionRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $correction, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Correction/PostCorrectionsRequest.php b/src/Requests/Correction/PostCorrectionsRequest.php index f574882..df0c3f5 100644 --- a/src/Requests/Correction/PostCorrectionsRequest.php +++ b/src/Requests/Correction/PostCorrectionsRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Customer/PatchCustomerRequest.php b/src/Requests/Customer/PatchCustomerRequest.php index a616dc2..1ed43ae 100644 --- a/src/Requests/Customer/PatchCustomerRequest.php +++ b/src/Requests/Customer/PatchCustomerRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $customer, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Customer/PostCustomersRequest.php b/src/Requests/Customer/PostCustomersRequest.php index 05bf5b1..ddff920 100644 --- a/src/Requests/Customer/PostCustomersRequest.php +++ b/src/Requests/Customer/PostCustomersRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Entry/PatchEntryRequest.php b/src/Requests/Entry/PatchEntryRequest.php index a0e174c..75ce82f 100644 --- a/src/Requests/Entry/PatchEntryRequest.php +++ b/src/Requests/Entry/PatchEntryRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $entry, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Entry/PostEntriesRequest.php b/src/Requests/Entry/PostEntriesRequest.php index b6fcad6..b0720dd 100644 --- a/src/Requests/Entry/PostEntriesRequest.php +++ b/src/Requests/Entry/PostEntriesRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Event/PostEventsRequest.php b/src/Requests/Event/PostEventsRequest.php index dab7d73..a992e98 100644 --- a/src/Requests/Event/PostEventsRequest.php +++ b/src/Requests/Event/PostEventsRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php index 7cb5c49..6f829e5 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $overtime, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php index da59935..21efb87 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $entry, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Team/PatchTeamRequest.php b/src/Requests/Team/PatchTeamRequest.php index b2d29c2..c58bbeb 100644 --- a/src/Requests/Team/PatchTeamRequest.php +++ b/src/Requests/Team/PatchTeamRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $team, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/Team/PostTeamsRequest.php b/src/Requests/Team/PostTeamsRequest.php index 6884d76..22b663f 100644 --- a/src/Requests/Team/PostTeamsRequest.php +++ b/src/Requests/Team/PostTeamsRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/User/PatchUserRequest.php b/src/Requests/User/PatchUserRequest.php index 787e346..29f1a2e 100644 --- a/src/Requests/User/PatchUserRequest.php +++ b/src/Requests/User/PatchUserRequest.php @@ -24,11 +24,11 @@ public function resolveEndpoint(): string public function __construct( protected string $user, - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Requests/User/PostUsersRequest.php b/src/Requests/User/PostUsersRequest.php index 9b24c2d..4efbafe 100644 --- a/src/Requests/User/PostUsersRequest.php +++ b/src/Requests/User/PostUsersRequest.php @@ -23,11 +23,11 @@ public function resolveEndpoint(): string } public function __construct( - protected Model $data, + protected Model|array|null $data, ) {} protected function defaultBody(): array { - return $this->data->toJsonApi(); + return $this->data ? $this->data->toJsonApi() : []; } } diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php index d233240..b260341 100644 --- a/src/Resource/Approve.php +++ b/src/Resource/Approve.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Approve\PostOvertimeApprove; +use Timatic\SDK\Requests\Approve\PostOvertimeApproveRequest; class Approve extends BaseResource { @@ -12,6 +12,6 @@ public function postOvertimeApprove( string $overtime, \Timatic\SDK\Foundation\Model|array|null $data = null, ): Response { - return $this->connector->send(new PostOvertimeApprove($overtime, $data)); + return $this->connector->send(new PostOvertimeApproveRequest($overtime, $data)); } } diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index 7504c91..5976c1d 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -4,11 +4,11 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Budget\DeleteBudget; -use Timatic\SDK\Requests\Budget\GetBudget; -use Timatic\SDK\Requests\Budget\GetBudgets; -use Timatic\SDK\Requests\Budget\PatchBudget; -use Timatic\SDK\Requests\Budget\PostBudgets; +use Timatic\SDK\Requests\Budget\DeleteBudgetRequest; +use Timatic\SDK\Requests\Budget\GetBudgetRequest; +use Timatic\SDK\Requests\Budget\GetBudgetsRequest; +use Timatic\SDK\Requests\Budget\PatchBudgetRequest; +use Timatic\SDK\Requests\Budget\PostBudgetsRequest; class Budget extends BaseResource { @@ -34,26 +34,26 @@ public function getBudgets( ?string $filtershowToCustomer = null, ?string $include = null, ): Response { - return $this->connector->send(new GetBudgets($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); + return $this->connector->send(new GetBudgetsRequest($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); } public function postBudgets(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostBudgets($data)); + return $this->connector->send(new PostBudgetsRequest($data)); } public function getBudget(string $budget): Response { - return $this->connector->send(new GetBudget($budget)); + return $this->connector->send(new GetBudgetRequest($budget)); } public function deleteBudget(string $budget): Response { - return $this->connector->send(new DeleteBudget($budget)); + return $this->connector->send(new DeleteBudgetRequest($budget)); } public function patchBudget(string $budget, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PatchBudget($budget, $data)); + return $this->connector->send(new PatchBudgetRequest($budget, $data)); } } diff --git a/src/Resource/BudgetTimeSpentTotal.php b/src/Resource/BudgetTimeSpentTotal.php index f920c8d..40ff14a 100644 --- a/src/Resource/BudgetTimeSpentTotal.php +++ b/src/Resource/BudgetTimeSpentTotal.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\BudgetTimeSpentTotal\GetBudgetTimeSpentTotals; +use Timatic\SDK\Requests\BudgetTimeSpentTotal\GetBudgetTimeSpentTotalsRequest; class BudgetTimeSpentTotal extends BaseResource { public function getBudgetTimeSpentTotals(?string $filterbudgetId = null, ?string $filterbudgetIdeq = null): Response { - return $this->connector->send(new GetBudgetTimeSpentTotals($filterbudgetId, $filterbudgetIdeq)); + return $this->connector->send(new GetBudgetTimeSpentTotalsRequest($filterbudgetId, $filterbudgetIdeq)); } } diff --git a/src/Resource/BudgetType.php b/src/Resource/BudgetType.php index 9727ada..9d73620 100644 --- a/src/Resource/BudgetType.php +++ b/src/Resource/BudgetType.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\BudgetType\GetBudgetTypes; +use Timatic\SDK\Requests\BudgetType\GetBudgetTypesRequest; class BudgetType extends BaseResource { public function getBudgetTypes(): Response { - return $this->connector->send(new GetBudgetTypes); + return $this->connector->send(new GetBudgetTypesRequest); } } diff --git a/src/Resource/Change.php b/src/Resource/Change.php index fffd7ff..2a06fdc 100644 --- a/src/Resource/Change.php +++ b/src/Resource/Change.php @@ -4,18 +4,18 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Change\GetChange; -use Timatic\SDK\Requests\Change\GetChanges; +use Timatic\SDK\Requests\Change\GetChangeRequest; +use Timatic\SDK\Requests\Change\GetChangesRequest; class Change extends BaseResource { public function getChange(string $change): Response { - return $this->connector->send(new GetChange($change)); + return $this->connector->send(new GetChangeRequest($change)); } public function getChanges(): Response { - return $this->connector->send(new GetChanges); + return $this->connector->send(new GetChangesRequest); } } diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index 25c3543..e2c03ac 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -4,18 +4,18 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Correction\PatchCorrection; -use Timatic\SDK\Requests\Correction\PostCorrections; +use Timatic\SDK\Requests\Correction\PatchCorrectionRequest; +use Timatic\SDK\Requests\Correction\PostCorrectionsRequest; class Correction extends BaseResource { public function postCorrections(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostCorrections($data)); + return $this->connector->send(new PostCorrectionsRequest($data)); } public function patchCorrection(string $correction, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PatchCorrection($correction, $data)); + return $this->connector->send(new PatchCorrectionRequest($correction, $data)); } } diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index ceb571e..4aaedae 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -4,36 +4,36 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Customer\DeleteCustomer; -use Timatic\SDK\Requests\Customer\GetCustomer; -use Timatic\SDK\Requests\Customer\GetCustomers; -use Timatic\SDK\Requests\Customer\PatchCustomer; -use Timatic\SDK\Requests\Customer\PostCustomers; +use Timatic\SDK\Requests\Customer\DeleteCustomerRequest; +use Timatic\SDK\Requests\Customer\GetCustomerRequest; +use Timatic\SDK\Requests\Customer\GetCustomersRequest; +use Timatic\SDK\Requests\Customer\PatchCustomerRequest; +use Timatic\SDK\Requests\Customer\PostCustomersRequest; class Customer extends BaseResource { public function getCustomers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response { - return $this->connector->send(new GetCustomers($filterexternalId, $filterexternalIdeq)); + return $this->connector->send(new GetCustomersRequest($filterexternalId, $filterexternalIdeq)); } public function postCustomers(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostCustomers($data)); + return $this->connector->send(new PostCustomersRequest($data)); } public function getCustomer(string $customer): Response { - return $this->connector->send(new GetCustomer($customer)); + return $this->connector->send(new GetCustomerRequest($customer)); } public function deleteCustomer(string $customer): Response { - return $this->connector->send(new DeleteCustomer($customer)); + return $this->connector->send(new DeleteCustomerRequest($customer)); } public function patchCustomer(string $customer, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PatchCustomer($customer, $data)); + return $this->connector->send(new PatchCustomerRequest($customer, $data)); } } diff --git a/src/Resource/DailyProgress.php b/src/Resource/DailyProgress.php index a125ff8..bac7243 100644 --- a/src/Resource/DailyProgress.php +++ b/src/Resource/DailyProgress.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\DailyProgress\GetDailyProgresses; +use Timatic\SDK\Requests\DailyProgress\GetDailyProgressesRequest; class DailyProgress extends BaseResource { public function getDailyProgresses(): Response { - return $this->connector->send(new GetDailyProgresses); + return $this->connector->send(new GetDailyProgressesRequest); } } diff --git a/src/Resource/EntriesExport.php b/src/Resource/EntriesExport.php index 12a1cb6..4dea7a5 100644 --- a/src/Resource/EntriesExport.php +++ b/src/Resource/EntriesExport.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\EntriesExport\GetBudgetEntriesExport; +use Timatic\SDK\Requests\EntriesExport\GetBudgetEntriesExportRequest; class EntriesExport extends BaseResource { @@ -79,6 +79,6 @@ public function getBudgetEntriesExport( ?string $filterisInvoiceable = null, ?string $include = null, ): Response { - return $this->connector->send(new GetBudgetEntriesExport($budget, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + return $this->connector->send(new GetBudgetEntriesExportRequest($budget, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); } } diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 9e7907a..61147ff 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -4,11 +4,11 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Entry\DeleteEntry; -use Timatic\SDK\Requests\Entry\GetEntries; -use Timatic\SDK\Requests\Entry\GetEntry; -use Timatic\SDK\Requests\Entry\PatchEntry; -use Timatic\SDK\Requests\Entry\PostEntries; +use Timatic\SDK\Requests\Entry\DeleteEntryRequest; +use Timatic\SDK\Requests\Entry\GetEntriesRequest; +use Timatic\SDK\Requests\Entry\GetEntryRequest; +use Timatic\SDK\Requests\Entry\PatchEntryRequest; +use Timatic\SDK\Requests\Entry\PostEntriesRequest; class Entry extends BaseResource { @@ -82,26 +82,26 @@ public function getEntries( ?string $filterisInvoiceable = null, ?string $include = null, ): Response { - return $this->connector->send(new GetEntries($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + return $this->connector->send(new GetEntriesRequest($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); } public function postEntries(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostEntries($data)); + return $this->connector->send(new PostEntriesRequest($data)); } public function getEntry(string $entry): Response { - return $this->connector->send(new GetEntry($entry)); + return $this->connector->send(new GetEntryRequest($entry)); } public function deleteEntry(string $entry): Response { - return $this->connector->send(new DeleteEntry($entry)); + return $this->connector->send(new DeleteEntryRequest($entry)); } public function patchEntry(string $entry, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PatchEntry($entry, $data)); + return $this->connector->send(new PatchEntryRequest($entry, $data)); } } diff --git a/src/Resource/EntrySuggestion.php b/src/Resource/EntrySuggestion.php index 217becd..4dd60af 100644 --- a/src/Resource/EntrySuggestion.php +++ b/src/Resource/EntrySuggestion.php @@ -4,9 +4,9 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\EntrySuggestion\DeleteEntrySuggestion; -use Timatic\SDK\Requests\EntrySuggestion\GetEntrySuggestion; -use Timatic\SDK\Requests\EntrySuggestion\GetEntrySuggestions; +use Timatic\SDK\Requests\EntrySuggestion\DeleteEntrySuggestionRequest; +use Timatic\SDK\Requests\EntrySuggestion\GetEntrySuggestionRequest; +use Timatic\SDK\Requests\EntrySuggestion\GetEntrySuggestionsRequest; class EntrySuggestion extends BaseResource { @@ -20,16 +20,16 @@ public function getEntrySuggestions( ?string $filterdatelte = null, ?string $filterdatecontains = null, ): Response { - return $this->connector->send(new GetEntrySuggestions($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); + return $this->connector->send(new GetEntrySuggestionsRequest($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); } public function getEntrySuggestion(string $entrySuggestion): Response { - return $this->connector->send(new GetEntrySuggestion($entrySuggestion)); + return $this->connector->send(new GetEntrySuggestionRequest($entrySuggestion)); } public function deleteEntrySuggestion(string $entrySuggestion): Response { - return $this->connector->send(new DeleteEntrySuggestion($entrySuggestion)); + return $this->connector->send(new DeleteEntrySuggestionRequest($entrySuggestion)); } } diff --git a/src/Resource/Event.php b/src/Resource/Event.php index 7b8f785..5b46e7d 100644 --- a/src/Resource/Event.php +++ b/src/Resource/Event.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Event\PostEvents; +use Timatic\SDK\Requests\Event\PostEventsRequest; class Event extends BaseResource { public function postEvents(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostEvents($data)); + return $this->connector->send(new PostEventsRequest($data)); } } diff --git a/src/Resource/ExportMail.php b/src/Resource/ExportMail.php index f151f0b..e3ee791 100644 --- a/src/Resource/ExportMail.php +++ b/src/Resource/ExportMail.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\ExportMail\GetBudgetsExportMails; +use Timatic\SDK\Requests\ExportMail\GetBudgetsExportMailsRequest; class ExportMail extends BaseResource { public function getBudgetsExportMails(): Response { - return $this->connector->send(new GetBudgetsExportMails); + return $this->connector->send(new GetBudgetsExportMailsRequest); } } diff --git a/src/Resource/Incident.php b/src/Resource/Incident.php index a342574..0c4b8b2 100644 --- a/src/Resource/Incident.php +++ b/src/Resource/Incident.php @@ -4,18 +4,18 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Incident\GetIncident; -use Timatic\SDK\Requests\Incident\GetIncidents; +use Timatic\SDK\Requests\Incident\GetIncidentRequest; +use Timatic\SDK\Requests\Incident\GetIncidentsRequest; class Incident extends BaseResource { public function getIncident(string $incident): Response { - return $this->connector->send(new GetIncident($incident)); + return $this->connector->send(new GetIncidentRequest($incident)); } public function getIncidents(): Response { - return $this->connector->send(new GetIncidents); + return $this->connector->send(new GetIncidentsRequest); } } diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php index 7bd6f26..810e2b7 100644 --- a/src/Resource/MarkAsExported.php +++ b/src/Resource/MarkAsExported.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\MarkAsExported\PostOvertimeMarkAsExported; +use Timatic\SDK\Requests\MarkAsExported\PostOvertimeMarkAsExportedRequest; class MarkAsExported extends BaseResource { @@ -12,6 +12,6 @@ public function postOvertimeMarkAsExported( string $overtime, \Timatic\SDK\Foundation\Model|array|null $data = null, ): Response { - return $this->connector->send(new PostOvertimeMarkAsExported($overtime, $data)); + return $this->connector->send(new PostOvertimeMarkAsExportedRequest($overtime, $data)); } } diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php index d9b6076..3338375 100644 --- a/src/Resource/MarkAsInvoiced.php +++ b/src/Resource/MarkAsInvoiced.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\MarkAsInvoiced\PostEntryMarkAsInvoiced; +use Timatic\SDK\Requests\MarkAsInvoiced\PostEntryMarkAsInvoicedRequest; class MarkAsInvoiced extends BaseResource { @@ -12,6 +12,6 @@ public function postEntryMarkAsInvoiced( string $entry, \Timatic\SDK\Foundation\Model|array|null $data = null, ): Response { - return $this->connector->send(new PostEntryMarkAsInvoiced($entry, $data)); + return $this->connector->send(new PostEntryMarkAsInvoicedRequest($entry, $data)); } } diff --git a/src/Resource/Me.php b/src/Resource/Me.php index b77c7fd..f5a933f 100644 --- a/src/Resource/Me.php +++ b/src/Resource/Me.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Me\GetMes; +use Timatic\SDK\Requests\Me\GetMesRequest; class Me extends BaseResource { public function getMes(): Response { - return $this->connector->send(new GetMes); + return $this->connector->send(new GetMesRequest); } } diff --git a/src/Resource/Number.php b/src/Resource/Number.php index 58ff377..d658e64 100644 --- a/src/Resource/Number.php +++ b/src/Resource/Number.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Number\GetIncidentsNumber; +use Timatic\SDK\Requests\Number\GetIncidentsNumberRequest; class Number extends BaseResource { public function getIncidentsNumber(string $incident): Response { - return $this->connector->send(new GetIncidentsNumber($incident)); + return $this->connector->send(new GetIncidentsNumberRequest($incident)); } } diff --git a/src/Resource/Overtime.php b/src/Resource/Overtime.php index ba9b360..a8b1edb 100644 --- a/src/Resource/Overtime.php +++ b/src/Resource/Overtime.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Overtime\GetOvertimes; +use Timatic\SDK\Requests\Overtime\GetOvertimesRequest; class Overtime extends BaseResource { @@ -36,6 +36,6 @@ public function getOvertimes( ?string $filterapprovedAtcontains = null, ?string $filterisExported = null, ): Response { - return $this->connector->send(new GetOvertimes($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); + return $this->connector->send(new GetOvertimesRequest($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); } } diff --git a/src/Resource/Period.php b/src/Resource/Period.php index 2ad8cb1..e35fb6b 100644 --- a/src/Resource/Period.php +++ b/src/Resource/Period.php @@ -4,12 +4,12 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Period\GetBudgetPeriods; +use Timatic\SDK\Requests\Period\GetBudgetPeriodsRequest; class Period extends BaseResource { public function getBudgetPeriods(string $budget): Response { - return $this->connector->send(new GetBudgetPeriods($budget)); + return $this->connector->send(new GetBudgetPeriodsRequest($budget)); } } diff --git a/src/Resource/Team.php b/src/Resource/Team.php index 256e25d..97854e7 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -4,36 +4,36 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\Team\DeleteTeam; -use Timatic\SDK\Requests\Team\GetTeam; -use Timatic\SDK\Requests\Team\GetTeams; -use Timatic\SDK\Requests\Team\PatchTeam; -use Timatic\SDK\Requests\Team\PostTeams; +use Timatic\SDK\Requests\Team\DeleteTeamRequest; +use Timatic\SDK\Requests\Team\GetTeamRequest; +use Timatic\SDK\Requests\Team\GetTeamsRequest; +use Timatic\SDK\Requests\Team\PatchTeamRequest; +use Timatic\SDK\Requests\Team\PostTeamsRequest; class Team extends BaseResource { public function getTeams(): Response { - return $this->connector->send(new GetTeams); + return $this->connector->send(new GetTeamsRequest); } public function postTeams(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostTeams($data)); + return $this->connector->send(new PostTeamsRequest($data)); } public function getTeam(string $team): Response { - return $this->connector->send(new GetTeam($team)); + return $this->connector->send(new GetTeamRequest($team)); } public function deleteTeam(string $team): Response { - return $this->connector->send(new DeleteTeam($team)); + return $this->connector->send(new DeleteTeamRequest($team)); } public function patchTeam(string $team, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PatchTeam($team, $data)); + return $this->connector->send(new PatchTeamRequest($team, $data)); } } diff --git a/src/Resource/TimeSpentTotal.php b/src/Resource/TimeSpentTotal.php index 0aeb9b6..c5b9fda 100644 --- a/src/Resource/TimeSpentTotal.php +++ b/src/Resource/TimeSpentTotal.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\TimeSpentTotal\GetTimeSpentTotals; +use Timatic\SDK\Requests\TimeSpentTotal\GetTimeSpentTotalsRequest; class TimeSpentTotal extends BaseResource { @@ -16,6 +16,6 @@ public function getTimeSpentTotals( ?string $filteruserId = null, ?string $filteruserIdeq = null, ): Response { - return $this->connector->send(new GetTimeSpentTotals($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); + return $this->connector->send(new GetTimeSpentTotalsRequest($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); } } diff --git a/src/Resource/User.php b/src/Resource/User.php index a0b3905..84e0799 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -4,36 +4,36 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\User\DeleteUser; -use Timatic\SDK\Requests\User\GetUser; -use Timatic\SDK\Requests\User\GetUsers; -use Timatic\SDK\Requests\User\PatchUser; -use Timatic\SDK\Requests\User\PostUsers; +use Timatic\SDK\Requests\User\DeleteUserRequest; +use Timatic\SDK\Requests\User\GetUserRequest; +use Timatic\SDK\Requests\User\GetUsersRequest; +use Timatic\SDK\Requests\User\PatchUserRequest; +use Timatic\SDK\Requests\User\PostUsersRequest; class User extends BaseResource { public function getUsers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response { - return $this->connector->send(new GetUsers($filterexternalId, $filterexternalIdeq)); + return $this->connector->send(new GetUsersRequest($filterexternalId, $filterexternalIdeq)); } public function postUsers(\Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PostUsers($data)); + return $this->connector->send(new PostUsersRequest($data)); } public function getUser(string $user): Response { - return $this->connector->send(new GetUser($user)); + return $this->connector->send(new GetUserRequest($user)); } public function deleteUser(string $user): Response { - return $this->connector->send(new DeleteUser($user)); + return $this->connector->send(new DeleteUserRequest($user)); } public function patchUser(string $user, \Timatic\SDK\Foundation\Model|array|null $data = null): Response { - return $this->connector->send(new PatchUser($user, $data)); + return $this->connector->send(new PatchUserRequest($user, $data)); } } diff --git a/src/Resource/UserCustomerHoursAggregate.php b/src/Resource/UserCustomerHoursAggregate.php index 2449903..99cedfb 100644 --- a/src/Resource/UserCustomerHoursAggregate.php +++ b/src/Resource/UserCustomerHoursAggregate.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Requests\UserCustomerHoursAggregate\GetUserCustomerHoursAggregates; +use Timatic\SDK\Requests\UserCustomerHoursAggregate\GetUserCustomerHoursAggregatesRequest; class UserCustomerHoursAggregate extends BaseResource { @@ -42,6 +42,6 @@ public function getUserCustomerHoursAggregates( ?string $filteruserIdlte = null, ?string $filteruserIdcontains = null, ): Response { - return $this->connector->send(new GetUserCustomerHoursAggregates($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); + return $this->connector->send(new GetUserCustomerHoursAggregatesRequest($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); } } diff --git a/src/TimaticConnector.php b/src/TimaticConnector.php index a5408ad..2022f9d 100644 --- a/src/TimaticConnector.php +++ b/src/TimaticConnector.php @@ -61,6 +61,8 @@ public function paginate(Request $request): JsonApiPaginator return new JsonApiPaginator($this, $request); } + public function __construct() {} + public function resolveBaseUrl(): string { return config('timatic.base_url'); diff --git a/tests/ApproveTest.php b/tests/ApproveTest.php new file mode 100644 index 0000000..59037cc --- /dev/null +++ b/tests/ApproveTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the postOvertimeApprove method in the Approve resource', function () { + Saloon::fake([ + PostOvertimeApproveRequest::class => MockResponse::fixture('approve.postOvertimeApprove'), + ]); + + $response = $this->timaticConnector->approve()->postOvertimeApprove( + overtime: 'test string' + ); + + Saloon::assertSent(PostOvertimeApproveRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/BudgetTest.php b/tests/BudgetTest.php new file mode 100644 index 0000000..1ae30db --- /dev/null +++ b/tests/BudgetTest.php @@ -0,0 +1,104 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getBudgets method in the Budget resource', function () { + Saloon::fake([ + GetBudgetsRequest::class => MockResponse::fixture('budget.getBudgets'), + ]); + + $response = $this->timaticConnector->budget()->getBudgets( + filtercustomerId: 123, + filtercustomerIdeq: 123, + filtercustomerIdnq: 123, + filtercustomerIdgt: 123, + filtercustomerIdlt: 123, + filtercustomerIdgte: 123, + filtercustomerIdlte: 123, + filtercustomerIdcontains: 123, + filterbudgetTypeId: 'test string', + filterbudgetTypeIdeq: 'test string', + filterbudgetTypeIdnq: 'test string', + filterbudgetTypeIdgt: 'test string', + filterbudgetTypeIdlt: 'test string', + filterbudgetTypeIdgte: 'test string', + filterbudgetTypeIdlte: 'test string', + filterbudgetTypeIdcontains: 'test string', + filterisArchived: 'test string', + filtercustomerExternalId: 'test string', + filtershowToCustomer: 'test string', + include: 'test string' + ); + + Saloon::assertSent(GetBudgetsRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the postBudgets method in the Budget resource', function () { + Saloon::fake([ + PostBudgetsRequest::class => MockResponse::fixture('budget.postBudgets'), + ]); + + $response = $this->timaticConnector->budget()->postBudgets( + + ); + + Saloon::assertSent(PostBudgetsRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getBudget method in the Budget resource', function () { + Saloon::fake([ + GetBudgetRequest::class => MockResponse::fixture('budget.getBudget'), + ]); + + $response = $this->timaticConnector->budget()->getBudget( + budget: 'test string' + ); + + Saloon::assertSent(GetBudgetRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the deleteBudget method in the Budget resource', function () { + Saloon::fake([ + DeleteBudgetRequest::class => MockResponse::fixture('budget.deleteBudget'), + ]); + + $response = $this->timaticConnector->budget()->deleteBudget( + budget: 'test string' + ); + + Saloon::assertSent(DeleteBudgetRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the patchBudget method in the Budget resource', function () { + Saloon::fake([ + PatchBudgetRequest::class => MockResponse::fixture('budget.patchBudget'), + ]); + + $response = $this->timaticConnector->budget()->patchBudget( + budget: 'test string' + ); + + Saloon::assertSent(PatchBudgetRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/BudgetTimeSpentTotalTest.php b/tests/BudgetTimeSpentTotalTest.php new file mode 100644 index 0000000..b0b9839 --- /dev/null +++ b/tests/BudgetTimeSpentTotalTest.php @@ -0,0 +1,26 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getBudgetTimeSpentTotals method in the BudgetTimeSpentTotal resource', function () { + Saloon::fake([ + GetBudgetTimeSpentTotalsRequest::class => MockResponse::fixture('budgetTimeSpentTotal.getBudgetTimeSpentTotals'), + ]); + + $response = $this->timaticConnector->budgetTimeSpentTotal()->getBudgetTimeSpentTotals( + filterbudgetId: 'test string', + filterbudgetIdeq: 'test string' + ); + + Saloon::assertSent(GetBudgetTimeSpentTotalsRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/BudgetTypeTest.php b/tests/BudgetTypeTest.php new file mode 100644 index 0000000..1014eda --- /dev/null +++ b/tests/BudgetTypeTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getBudgetTypes method in the BudgetType resource', function () { + Saloon::fake([ + GetBudgetTypesRequest::class => MockResponse::fixture('budgetType.getBudgetTypes'), + ]); + + $response = $this->timaticConnector->budgetType()->getBudgetTypes( + + ); + + Saloon::assertSent(GetBudgetTypesRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/ChangeTest.php b/tests/ChangeTest.php new file mode 100644 index 0000000..f76111a --- /dev/null +++ b/tests/ChangeTest.php @@ -0,0 +1,40 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getChange method in the Change resource', function () { + Saloon::fake([ + GetChangeRequest::class => MockResponse::fixture('change.getChange'), + ]); + + $response = $this->timaticConnector->change()->getChange( + change: 'test string' + ); + + Saloon::assertSent(GetChangeRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getChanges method in the Change resource', function () { + Saloon::fake([ + GetChangesRequest::class => MockResponse::fixture('change.getChanges'), + ]); + + $response = $this->timaticConnector->change()->getChanges( + + ); + + Saloon::assertSent(GetChangesRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/CorrectionTest.php b/tests/CorrectionTest.php new file mode 100644 index 0000000..aa15e53 --- /dev/null +++ b/tests/CorrectionTest.php @@ -0,0 +1,40 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the postCorrections method in the Correction resource', function () { + Saloon::fake([ + PostCorrectionsRequest::class => MockResponse::fixture('correction.postCorrections'), + ]); + + $response = $this->timaticConnector->correction()->postCorrections( + + ); + + Saloon::assertSent(PostCorrectionsRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the patchCorrection method in the Correction resource', function () { + Saloon::fake([ + PatchCorrectionRequest::class => MockResponse::fixture('correction.patchCorrection'), + ]); + + $response = $this->timaticConnector->correction()->patchCorrection( + correction: 'test string' + ); + + Saloon::assertSent(PatchCorrectionRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/CustomerTest.php b/tests/CustomerTest.php new file mode 100644 index 0000000..64dbad1 --- /dev/null +++ b/tests/CustomerTest.php @@ -0,0 +1,86 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getCustomers method in the Customer resource', function () { + Saloon::fake([ + GetCustomersRequest::class => MockResponse::fixture('customer.getCustomers'), + ]); + + $response = $this->timaticConnector->customer()->getCustomers( + filterexternalId: 'test string', + filterexternalIdeq: 'test string' + ); + + Saloon::assertSent(GetCustomersRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the postCustomers method in the Customer resource', function () { + Saloon::fake([ + PostCustomersRequest::class => MockResponse::fixture('customer.postCustomers'), + ]); + + $response = $this->timaticConnector->customer()->postCustomers( + + ); + + Saloon::assertSent(PostCustomersRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getCustomer method in the Customer resource', function () { + Saloon::fake([ + GetCustomerRequest::class => MockResponse::fixture('customer.getCustomer'), + ]); + + $response = $this->timaticConnector->customer()->getCustomer( + customer: 'test string' + ); + + Saloon::assertSent(GetCustomerRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the deleteCustomer method in the Customer resource', function () { + Saloon::fake([ + DeleteCustomerRequest::class => MockResponse::fixture('customer.deleteCustomer'), + ]); + + $response = $this->timaticConnector->customer()->deleteCustomer( + customer: 'test string' + ); + + Saloon::assertSent(DeleteCustomerRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the patchCustomer method in the Customer resource', function () { + Saloon::fake([ + PatchCustomerRequest::class => MockResponse::fixture('customer.patchCustomer'), + ]); + + $response = $this->timaticConnector->customer()->patchCustomer( + customer: 'test string' + ); + + Saloon::assertSent(PatchCustomerRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/DailyProgressTest.php b/tests/DailyProgressTest.php new file mode 100644 index 0000000..69dd127 --- /dev/null +++ b/tests/DailyProgressTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getDailyProgresses method in the DailyProgress resource', function () { + Saloon::fake([ + GetDailyProgressesRequest::class => MockResponse::fixture('dailyProgress.getDailyProgresses'), + ]); + + $response = $this->timaticConnector->dailyProgress()->getDailyProgresses( + + ); + + Saloon::assertSent(GetDailyProgressesRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/EntriesExportTest.php b/tests/EntriesExportTest.php new file mode 100644 index 0000000..0f7bef9 --- /dev/null +++ b/tests/EntriesExportTest.php @@ -0,0 +1,93 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getBudgetEntriesExport method in the EntriesExport resource', function () { + Saloon::fake([ + GetBudgetEntriesExportRequest::class => MockResponse::fixture('entriesExport.getBudgetEntriesExport'), + ]); + + $response = $this->timaticConnector->entriesExport()->getBudgetEntriesExport( + budget: 'test string', + filteruserId: 'test string', + filteruserIdeq: 'test string', + filteruserIdnq: 'test string', + filteruserIdgt: 'test string', + filteruserIdlt: 'test string', + filteruserIdgte: 'test string', + filteruserIdlte: 'test string', + filteruserIdcontains: 'test string', + filterbudgetId: 'test string', + filterbudgetIdeq: 'test string', + filterbudgetIdnq: 'test string', + filterbudgetIdgt: 'test string', + filterbudgetIdlt: 'test string', + filterbudgetIdgte: 'test string', + filterbudgetIdlte: 'test string', + filterbudgetIdcontains: 'test string', + filterstartedAt: 'test string', + filterstartedAteq: 'test string', + filterstartedAtnq: 'test string', + filterstartedAtgt: 'test string', + filterstartedAtlt: 'test string', + filterstartedAtgte: 'test string', + filterstartedAtlte: 'test string', + filterstartedAtcontains: 'test string', + filterendedAt: 'test string', + filterendedAteq: 'test string', + filterendedAtnq: 'test string', + filterendedAtgt: 'test string', + filterendedAtlt: 'test string', + filterendedAtgte: 'test string', + filterendedAtlte: 'test string', + filterendedAtcontains: 'test string', + filterhasOvertime: 'test string', + filterhasOvertimeeq: 'test string', + filterhasOvertimenq: 'test string', + filterhasOvertimegt: 'test string', + filterhasOvertimelt: 'test string', + filterhasOvertimegte: 'test string', + filterhasOvertimelte: 'test string', + filterhasOvertimecontains: 'test string', + filteruserFullName: 'test string', + filteruserFullNameeq: 'test string', + filteruserFullNamenq: 'test string', + filteruserFullNamegt: 'test string', + filteruserFullNamelt: 'test string', + filteruserFullNamegte: 'test string', + filteruserFullNamelte: 'test string', + filteruserFullNamecontains: 'test string', + filtercustomerId: 'test string', + filtercustomerIdeq: 'test string', + filtercustomerIdnq: 'test string', + filtercustomerIdgt: 'test string', + filtercustomerIdlt: 'test string', + filtercustomerIdgte: 'test string', + filtercustomerIdlte: 'test string', + filtercustomerIdcontains: 'test string', + filterticketNumber: 'test string', + filterticketNumbereq: 'test string', + filterticketNumbernq: 'test string', + filterticketNumbergt: 'test string', + filterticketNumberlt: 'test string', + filterticketNumbergte: 'test string', + filterticketNumberlte: 'test string', + filterticketNumbercontains: 'test string', + filtersettlement: 'test string', + filterisInvoiced: 'test string', + filterisInvoiceable: 'test string', + include: 'test string' + ); + + Saloon::assertSent(GetBudgetEntriesExportRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/EntrySuggestionTest.php b/tests/EntrySuggestionTest.php new file mode 100644 index 0000000..9ddc995 --- /dev/null +++ b/tests/EntrySuggestionTest.php @@ -0,0 +1,62 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getEntrySuggestions method in the EntrySuggestion resource', function () { + Saloon::fake([ + GetEntrySuggestionsRequest::class => MockResponse::fixture('entrySuggestion.getEntrySuggestions'), + ]); + + $response = $this->timaticConnector->entrySuggestion()->getEntrySuggestions( + filterdate: 'test string', + filterdateeq: 'test string', + filterdatenq: 'test string', + filterdategt: 'test string', + filterdatelt: 'test string', + filterdategte: 'test string', + filterdatelte: 'test string', + filterdatecontains: 'test string' + ); + + Saloon::assertSent(GetEntrySuggestionsRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getEntrySuggestion method in the EntrySuggestion resource', function () { + Saloon::fake([ + GetEntrySuggestionRequest::class => MockResponse::fixture('entrySuggestion.getEntrySuggestion'), + ]); + + $response = $this->timaticConnector->entrySuggestion()->getEntrySuggestion( + entrySuggestion: 'test string' + ); + + Saloon::assertSent(GetEntrySuggestionRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the deleteEntrySuggestion method in the EntrySuggestion resource', function () { + Saloon::fake([ + DeleteEntrySuggestionRequest::class => MockResponse::fixture('entrySuggestion.deleteEntrySuggestion'), + ]); + + $response = $this->timaticConnector->entrySuggestion()->deleteEntrySuggestion( + entrySuggestion: 'test string' + ); + + Saloon::assertSent(DeleteEntrySuggestionRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/EntryTest.php b/tests/EntryTest.php new file mode 100644 index 0000000..6180977 --- /dev/null +++ b/tests/EntryTest.php @@ -0,0 +1,152 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getEntries method in the Entry resource', function () { + Saloon::fake([ + GetEntriesRequest::class => MockResponse::fixture('entry.getEntries'), + ]); + + $response = $this->timaticConnector->entry()->getEntries( + filteruserId: 'test string', + filteruserIdeq: 'test string', + filteruserIdnq: 'test string', + filteruserIdgt: 'test string', + filteruserIdlt: 'test string', + filteruserIdgte: 'test string', + filteruserIdlte: 'test string', + filteruserIdcontains: 'test string', + filterbudgetId: 'test string', + filterbudgetIdeq: 'test string', + filterbudgetIdnq: 'test string', + filterbudgetIdgt: 'test string', + filterbudgetIdlt: 'test string', + filterbudgetIdgte: 'test string', + filterbudgetIdlte: 'test string', + filterbudgetIdcontains: 'test string', + filterstartedAt: 'test string', + filterstartedAteq: 'test string', + filterstartedAtnq: 'test string', + filterstartedAtgt: 'test string', + filterstartedAtlt: 'test string', + filterstartedAtgte: 'test string', + filterstartedAtlte: 'test string', + filterstartedAtcontains: 'test string', + filterendedAt: 'test string', + filterendedAteq: 'test string', + filterendedAtnq: 'test string', + filterendedAtgt: 'test string', + filterendedAtlt: 'test string', + filterendedAtgte: 'test string', + filterendedAtlte: 'test string', + filterendedAtcontains: 'test string', + filterhasOvertime: 'test string', + filterhasOvertimeeq: 'test string', + filterhasOvertimenq: 'test string', + filterhasOvertimegt: 'test string', + filterhasOvertimelt: 'test string', + filterhasOvertimegte: 'test string', + filterhasOvertimelte: 'test string', + filterhasOvertimecontains: 'test string', + filteruserFullName: 'test string', + filteruserFullNameeq: 'test string', + filteruserFullNamenq: 'test string', + filteruserFullNamegt: 'test string', + filteruserFullNamelt: 'test string', + filteruserFullNamegte: 'test string', + filteruserFullNamelte: 'test string', + filteruserFullNamecontains: 'test string', + filtercustomerId: 'test string', + filtercustomerIdeq: 'test string', + filtercustomerIdnq: 'test string', + filtercustomerIdgt: 'test string', + filtercustomerIdlt: 'test string', + filtercustomerIdgte: 'test string', + filtercustomerIdlte: 'test string', + filtercustomerIdcontains: 'test string', + filterticketNumber: 'test string', + filterticketNumbereq: 'test string', + filterticketNumbernq: 'test string', + filterticketNumbergt: 'test string', + filterticketNumberlt: 'test string', + filterticketNumbergte: 'test string', + filterticketNumberlte: 'test string', + filterticketNumbercontains: 'test string', + filtersettlement: 'test string', + filterisInvoiced: 'test string', + filterisInvoiceable: 'test string', + include: 'test string' + ); + + Saloon::assertSent(GetEntriesRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the postEntries method in the Entry resource', function () { + Saloon::fake([ + PostEntriesRequest::class => MockResponse::fixture('entry.postEntries'), + ]); + + $response = $this->timaticConnector->entry()->postEntries( + + ); + + Saloon::assertSent(PostEntriesRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getEntry method in the Entry resource', function () { + Saloon::fake([ + GetEntryRequest::class => MockResponse::fixture('entry.getEntry'), + ]); + + $response = $this->timaticConnector->entry()->getEntry( + entry: 'test string' + ); + + Saloon::assertSent(GetEntryRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the deleteEntry method in the Entry resource', function () { + Saloon::fake([ + DeleteEntryRequest::class => MockResponse::fixture('entry.deleteEntry'), + ]); + + $response = $this->timaticConnector->entry()->deleteEntry( + entry: 'test string' + ); + + Saloon::assertSent(DeleteEntryRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the patchEntry method in the Entry resource', function () { + Saloon::fake([ + PatchEntryRequest::class => MockResponse::fixture('entry.patchEntry'), + ]); + + $response = $this->timaticConnector->entry()->patchEntry( + entry: 'test string' + ); + + Saloon::assertSent(PatchEntryRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/EventTest.php b/tests/EventTest.php new file mode 100644 index 0000000..74e3064 --- /dev/null +++ b/tests/EventTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the postEvents method in the Event resource', function () { + Saloon::fake([ + PostEventsRequest::class => MockResponse::fixture('event.postEvents'), + ]); + + $response = $this->timaticConnector->event()->postEvents( + + ); + + Saloon::assertSent(PostEventsRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/ExportMailTest.php b/tests/ExportMailTest.php new file mode 100644 index 0000000..d5e8fe2 --- /dev/null +++ b/tests/ExportMailTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getBudgetsExportMails method in the ExportMail resource', function () { + Saloon::fake([ + GetBudgetsExportMailsRequest::class => MockResponse::fixture('exportMail.getBudgetsExportMails'), + ]); + + $response = $this->timaticConnector->exportMail()->getBudgetsExportMails( + + ); + + Saloon::assertSent(GetBudgetsExportMailsRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/Fixtures/Saloon/approve.postOvertimeApprove.json b/tests/Fixtures/Saloon/approve.postOvertimeApprove.json new file mode 100644 index 0000000..77f88f0 --- /dev/null +++ b/tests/Fixtures/Saloon/approve.postOvertimeApprove.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Ijh2ZzVGRDRqU0tzbjZVNVZEd0YyMlE9PSIsInZhbHVlIjoiZSsyT1VFVzBGWlRhd3NqdUdVaGx6V0Via0hQeW1nbHNURVQvb2FoYWxQYThqM3FjcXUrVytJbjR1RklRcjZsTk5qdkpiQXYrYVhzZzZ2dmNWR3BZNG5QWXRjOHFTc2RKcmp3WkN1R3RRYkExdENCbHRaalQvNHcvRFhuR29BSEwiLCJtYWMiOiIwNjU3ZGU0YzZmMjg0ZmU0MGU2Yzc0NTg4ZjExNTY1YzJlNDQwZTZhYWU3MTA0ZTM0ODhkOTAzOTBhNWVhNmMyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Im04K3oyZmpNajE5bEt6UFVmcE1VM1E9PSIsInZhbHVlIjoieDJZZ1VGeSthSEdFZHZkOXM1SlBlT0JpSXhyM2U1MmFwNVg0LzZPNlY4SEFCc3JwRHdsZ0NyamsyV3hBNWVrQkVsU01teXZ5ckFHUmVwTFhOVmNFSEsxcGRZZFNRdVR1dUMzZnQ2Sk1wZmVRdWhyMlhMNXpHeHdramZsbmNTcisiLCJtYWMiOiJhNmQ2NTY2YWU0NzAzMzQzMzU5YTFhZjllMzIzNzc3ZThkZTUzMDAzM2EyMjVkYWRkMDRiNzAxNDQzMmUxODk0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.deleteBudget.json b/tests/Fixtures/Saloon/budget.deleteBudget.json new file mode 100644 index 0000000..faf4a68 --- /dev/null +++ b/tests/Fixtures/Saloon/budget.deleteBudget.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImQvWkc5cVh0cjZvSDE5YWNQTGpPUmc9PSIsInZhbHVlIjoiR1BaYUkwRWZqSWZaK21mT0FJQnBUemRvVGdtaytZK3FORXdaL1RCelJYdTgvakk2MzRtTm9rdmFqSFMrZFVPOUlVRHIrWVR6MFQ3QzRGVnZnamloK3N5K3Mvd0JybC90UnVrY1hxc1pNRE1BRXNiYUoyMHRsR1U0YjRRTllBaGciLCJtYWMiOiI3YzQxZmJjYTkxOGQxYjJjNzUyM2Q4ZTNmYzRjZjQ0ZDc0NmZkM2I5ZDI0ZWY2YTkwNWRmYzk3MWFiNjY3NjE2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IklGQzdSQ1VsRGRuN3FIM3hudkJFMnc9PSIsInZhbHVlIjoicC9JOHdBTlRkMFBITlRCdWhITTgvY3JJNkVXRUFQcU5qNXNWNkVmcjd1Vm1tZHZxKzhNU280UVJ2UDF4bWNXSXZIeXVpVU5oQnFYd0FpY2NhUlE3dEVGVWdvRHRUUVZaZ3R2NG93dXRTWVJIdWxkenRxb21pWDVaeVJjVFI2ajgiLCJtYWMiOiI4NTFmN2UyZjM4MzQ4NGE4ODJkY2IxZDFlZDQwODk2ODlmMWFlMTU5MWI1MzA4ZDhmM2UxYWNiNWI4MzA1M2VkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.getBudget.json b/tests/Fixtures/Saloon/budget.getBudget.json new file mode 100644 index 0000000..d740395 --- /dev/null +++ b/tests/Fixtures/Saloon/budget.getBudget.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Ijg1U1hoRkNCNUwweUlpZWFxaUtsMVE9PSIsInZhbHVlIjoiK1lIQWFCS1lPTmFIbzZXRnZEZ2ZqeWVJSWthVExXN3V0azRwRnZxV29GdTNTVkxHVC9BSGpjMHIrMW5kZFpUYXFDaWF0bkZEKzZ0VUQ1LzZhWkc5NzdyNXBWUzFQenpHYW5PSVF1R1ZsN3VLNTZQbStab3BmcXkvN0xuWGxlcUkiLCJtYWMiOiJkNmM4YWVjMTA1NTk4NWVjOGIyZjdmOGEyMWVjZDA3MzQxNGNkN2M0NDdhNDg3Y2EyMGNlNzhlMmQwYWIwYWE2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IjJGeVl1b1FHSk1KK0ZVZkhxSG5rTVE9PSIsInZhbHVlIjoiWVFXdExTMnpPQlRBRENsdXdCN0ZuL1dHODd5OGFIcjFaRU9Ob09VQkNqYTBMaGFuYTNHeTQ1dGtsRUVWYmRQV0FZS1pIOTRldGV3a0NWUEc0ckhiV1ZJVTBVUk1KSStVNVY2aHBZeElGdEdCbWt2QTIwMG1ubmg2SUx0N09FSWciLCJtYWMiOiJhNjJhM2I3ODMyM2U3NmU2NTMyMWQ1NWQxNmZiNzljMWEzZTNjZTgwYjE5ZjU3NDUwOWM0ODM5ZDVlNTkxZThlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.getBudgets.json b/tests/Fixtures/Saloon/budget.getBudgets.json new file mode 100644 index 0000000..ecdfb7e --- /dev/null +++ b/tests/Fixtures/Saloon/budget.getBudgets.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlRWbkVXVFlKRThLUVF3RERGeDBIUHc9PSIsInZhbHVlIjoiN0hpaG0razg3c3BOTDlUTE00YjQxWXg0N0lub08ySnZGSjF3YlRBLzZ3bWtvNVZBOTREVC9vSmVjOE9JZytZdUxiT3dVNmZROHd3d0F4NHA1UHJuSFVMZEJ0eGpKUUhQaXVSZXg1U2g3UEo4UVNxWEZCQVExSlFvRFI2WnlhbHMiLCJtYWMiOiIzZTA4YTczNjcyMDU0ZWM0MTM0ZjZjYmJkYzBhYjliMzk4NTNmYjRlMzc0YWNmZTg5NzIxYzc0ODg5NjFjMDg2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6ImNlUnZ5aDlvTzlZdkNSdHBnbjZ6cUE9PSIsInZhbHVlIjoiOUROSXpNUzVhZmxuazRNQ0R5ZmZHMFV2WjU3VHZiRVM5V0tDNUJJR081c0Z6WWVSaHVoNzV6VmdvWUlVS0hDSUNiZzFnYzAxWTFaOERKa285ZUx4SzA4RFUxa05JZzZNRFpMeGRyODBTa3Ruc0JlVEpiZXJmb3JhUCtrYVhFWFkiLCJtYWMiOiI0MGUxZDJmOGYwNGI4YjBhYmQzZmQ4N2E3MzIzM2M5Mzg3ODVjMDA0MDE3NzlmZjc4MTFkZmI2YjY4NWZhYzZlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.patchBudget.json b/tests/Fixtures/Saloon/budget.patchBudget.json new file mode 100644 index 0000000..2e1e0ee --- /dev/null +++ b/tests/Fixtures/Saloon/budget.patchBudget.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImJaeGVDeVJhMWpkcFl6bGFDcHFCYkE9PSIsInZhbHVlIjoiNnhGV2lyZ0liZlhNT2grODJlY21PWGJkZEw2WjZ2ZDFJOFRMcUZqa3lROHlLMDc2TjZHbzlPRVkxZDkzZUJLQ3ZVVnJ5TW1ZSTd3bExVazlLa2hSWmFKVy9oekxJVXM3UW5HRGhjdnRwRTZjNHpwTU9EQSt1WlM2cTlSTVVwNC8iLCJtYWMiOiJhZmE2ZjMxOGUxZDcxOWFkMjAzZDMyOTE1MzQyN2YxZWE5ZGViMTExMzE2MmM1NzhjZDVhNDZjNjg1OTU2M2EyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Ik1RcVdLNm12VEpCVmF4R3d0VWx5MGc9PSIsInZhbHVlIjoicnhtRlljZThMaWozc0x3ZjZFL2h5eFR5T0d6T250QmwzZEpJZmdTOUlrSkE4Z2RMSlp3M3dHK3pCSDhhelc2ZmNjUFlSQ080Q04yQVJsTnVHN1hXc0lPK25XdGhGdlVKeTgrQmR4cmZXS1o5MzJ2R1ZPYUc2cHNYdGNKQW53b2kiLCJtYWMiOiIxZTYxMmJjMmEyMjk1YmVlYzhkYTM2ZWEyZTQ5NjU3MmZjYTNlNjY4NWY4Y2NmMWI3Zjc4OTIzN2IxMzgyNGViIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.postBudgets.json b/tests/Fixtures/Saloon/budget.postBudgets.json new file mode 100644 index 0000000..131433a --- /dev/null +++ b/tests/Fixtures/Saloon/budget.postBudgets.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlBVb216cHhxaCtzSW1aMGtvSmNvV2c9PSIsInZhbHVlIjoiWXJCM1RaSzBocFJ2akt3ejI3TUEzV3RQYk1lY28ra1F1VW1rWVJpcHM1NnM2OUtZbHRsOEV2L3UwMTJUeHZKai9ORDZ2MGhDaVA1QUxlcXFGWXZBNmhEVUVqT1c3ZlQ3WlBEY2d0WTNZYXVEaUxBbjFGSWdEWW8xYTErL0JXUTAiLCJtYWMiOiI3ZWUxZjUwNDhiYTRhZDk2YWQ5OTBkNWE1MjM5MzhlM2Q3MGY4YTk5ZGI5NTQwOWE4NTQzNjAxMmEzZWM3Zjg0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InkwbzdralJ3RitDbXo0bmN4YWZjc1E9PSIsInZhbHVlIjoiS09KNitHTHYxckVnY2ViTVJxdHNXS01Lb25nZExhRnFEbDhXUHRzUnR1bnhXUHowYWZmaGQzbzNwUHJXemZCNmZrdkdXcVAzYnZSNHVvdTl1N1hpZDg5eFhOcU55aWd3aFBSc05oVlFtZ200ampHaFJjU0VxY296b3hHTk8ycG4iLCJtYWMiOiIwMzk2MGFiNDViYmIwNWM2MmYwODgzNWM4MmQ0Y2Q2MjMzODkwMWVjYmUzYzQ1YmMyMWNhNTEwOWFlYjlhNzFiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json b/tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json new file mode 100644 index 0000000..82daa5c --- /dev/null +++ b/tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IkRnaVlWRmM5aml3UUxsYk1RaGprdnc9PSIsInZhbHVlIjoiTUlWeXJna09DZFdReWtmNENxRjg1NEhhTkE2YnQ1WmlEcFBUUHJXQlRXWkRZaHhZRWorZ3JGTHduRnFobFlia3g2Vk9hSnlHZVB5MGNoNjN4STFueG9EN1VMMGs3Unc2dEp4RHh1bmlPNk8yNENYTjNuU3pBb2hIQ2JwMklHTlQiLCJtYWMiOiJiOTVmMGI2YmM3OWU3MGUyYzNhODY4YWIyYjQ4ZWZjOGQ3N2NlZjI2NTg0ZjQzNzZmMmQ5NjA4YTVjYzc5MGQ2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Im9wNml3Rm4zOWl6dlY4Ly83dmx1dXc9PSIsInZhbHVlIjoibWdoQmF5bWZCcytCZDhYZmpNWEpUdC93SGRITmpJd3drUUVUYXBWSjJJdWI0d1VRTm1KZlZvaVFKT2xzTVhwQTJoZDNnQldNOGhRbXJnbFU4UC9PTDF0d2F0Nk03UVJOZ0VQZklvTVFJRWJSNWU3RUhpSTZtTklXYUFHaFpaV2UiLCJtYWMiOiI4YjNjMjkxMmEzOTI1YmY2NjdhZWE1MDVlNjBmMTUxOWZlNzUxZmYyMmM4Y2JmOThlOWE0NGE5YmQzMDYxOTcwIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budgetType.getBudgetTypes.json b/tests/Fixtures/Saloon/budgetType.getBudgetTypes.json new file mode 100644 index 0000000..0c1e31c --- /dev/null +++ b/tests/Fixtures/Saloon/budgetType.getBudgetTypes.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IkRDN1Z2N2pRNUNMVW1Ec09RZ1c3WXc9PSIsInZhbHVlIjoiKys3bURqcUZFVytMUk5uUEtBdW5mNFJrTlhCUkN5MGtEMEt2UnJlTCs5RkdBUFQ3N1V5YUtBZWJUUjMrUkd3dngyRUZOQ0RYZmlyVG1nc0puc1J4SzFXeFJ3NjNNMy8ybkVuRFQvY09UZmJuRVl4c29Ga0piWFRQclFISzIzNXMiLCJtYWMiOiJlNDI2YjI5MzdkYjYzZWZjZGY4ZGY4YWY0MDM2YjAxOWE3N2I2ZmQ2Yjg0YzkzYmJkOGJhOTBjZmJhYmNlNGFkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IldnN1ZPTmp1SDVnSGRTdWFjLy9nalE9PSIsInZhbHVlIjoiT1d0eFhyZVV3RURMNVFJdGpFWklwREk0TmYyRlpiNFZYam9vMnFHNlhMZWNlek9IUFBUR2k4Q1lmQ2xTNnU3L1RydjRHMWMzWEFiSm5zcCs3WkFyYkkzeTlTU1RLbEc5UTJJWXFXdVI1cnZIS2JiWS9sZEtxeEFnK2ZGTUp3bzAiLCJtYWMiOiI0MzVhMDc5ODgxZDdjMWExOTUzMmY2ZThiZWU4NDdiNTdlOTBlNjc0MTM2NDY4ZTFmZjE5NzdhOGEwY2ExYmZhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/change.getChange.json b/tests/Fixtures/Saloon/change.getChange.json new file mode 100644 index 0000000..4edf0dc --- /dev/null +++ b/tests/Fixtures/Saloon/change.getChange.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImU1SDBoYUpuSmRXVTdPcHdPSHhHWVE9PSIsInZhbHVlIjoicU5jMGs3Qk9reGQ2bDIxNnhwQnVXTWIwUkY4RTZJYVA5Wlh4OENjQlhNR2R6Zmphckx6UUdHeDhyMURFNjNtc0M5RTZPMkZxb1ZVRFc2dXMwSXpFVlNtVVFpY1Jqdnd3UUJTa0I5aU00SlIxMUpIY05lV0dXeTJaSFMvMlp5OXoiLCJtYWMiOiI5MDdiZGEyZjRjYjIyOTczZGM4M2ZhYTU3YjlkNGM3YTA3YTY4NDhjYTZlOWQ5OWJkZDRmMmFkZjkyYzdlMWYyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkMzTnl4ekhiTUJhaS9JbzlvcTAzTnc9PSIsInZhbHVlIjoiY2hwNFNzTFJ2QTAwS3B0RjlmUE1IWk9Wb3RVK0pZMU1jL0tXODJHRDV2aU5POHh6YjdiTy9PMlFrZE5qd3ZMSzJNdVhmdG1aT0hMT0pGZFZBTlMySCtLS3lFM3dKd3JNVW1lTGY4b2RKV01qQkJFREhxOEszRnN1UkUyNGY3eXgiLCJtYWMiOiIwMTI5YTJmMGI0NTgwN2RmYzVlYjg2YzJmNzFhODAzYmE4MDM5YTgxZTMzODIxYWQ4Nzk4ZWQyZmFlMzdiNGZjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/change.getChanges.json b/tests/Fixtures/Saloon/change.getChanges.json new file mode 100644 index 0000000..e80281f --- /dev/null +++ b/tests/Fixtures/Saloon/change.getChanges.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlZuSU5aU09FMjJMU0tZZ2hNUTZuYkE9PSIsInZhbHVlIjoiTERINlBJdWc3RVVCUGRMeE9xSU44U1Y2dkJnNnZseWpZMWdEb3lSdG5RQThTVHZiMU5LVWpUc1l2eVdWQ0RPbGt6MDg2aGxIS3lNSWJwZ2hJOVQwU3F1aUlnaGpNQlNwRFRRNnhkRVI2U0hCb0ViY0lZeTgra2hPLzBoRUd3L2MiLCJtYWMiOiIwNTc2ZmU1ZDhlOWU0NTEzY2YyODBiMmQzYjI0ZDJiMjc0OWNjOWM4Nzg4MzJkNjdjMGVlN2E0ZTBiZGY2NzQ5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6ImdVNEIvQldZYzJrL3E4eEo5aDlkTHc9PSIsInZhbHVlIjoiRUxmejBPN2ZRSkt2NlBWanlkTlo4VEJLYzhLRlhlKzYvOHBDdlVHeGFvSHBzWkVybUZERENnZUV4a0xyMEJkUVJ5R0FiWlBHdkVRa0U5ODdBQU1MUWU5STNEQ1JZUmRaZDZPRFpqZTc0ZUZTbHh3MTFIS0JHWjFCZDN3RXc3d2ciLCJtYWMiOiJjZGNlY2RjZjhlZjU1ZmMyOGQ3YWFlMjMzZGU3MmIwNjk4OWM4N2E2MDBmYzQ0MzZlNDJlYjZmNzE4MjY0ZDIzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/correction.patchCorrection.json b/tests/Fixtures/Saloon/correction.patchCorrection.json new file mode 100644 index 0000000..39d69d2 --- /dev/null +++ b/tests/Fixtures/Saloon/correction.patchCorrection.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImhhUjhMN3NMOGg3bFl4T0hBV0pZNkE9PSIsInZhbHVlIjoiNlRERkVNK1hHTHEwUXpTenhsWFlzdUdYYmJkRnVydlpCS2dPTFlqelBOTmNQekNhWEpjSmhhNlZnMlc1ODQ2emRldFVWK0VlRml2Mkw4d3Fkd2RSZnFuSGQ1eXJOUGNjaW13MGZLeXcwRlZ6WlczS3VXaWVWa1Z1bitvek9PY0QiLCJtYWMiOiIwNDI4MWMyZjRhNzJhYzliMTQwYTRlN2RmMzk2MWRhMzJmODgxZWMyOTQ1YzQ5Nzg2M2MwODk2MjZhOGEzYzBlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkgrbHFORG1rTzZ0R1RkMlZkL3o2ZWc9PSIsInZhbHVlIjoiQTJXeUU3Z0t5NExoOVhZTWtSekNkNldBSjUrOXU3TUFKN1Z0UzdTYVk2ajhnV1dhNWJ1cGlKR0xWVjVhUDJ5WDk4dlBCdHRBWkdVRXZwUTQrM0xqeEZ6Yk9ZaTFEeE56MWZHbk9HYWJBUGhXNjVqOUtHZ0ZFMER6ZlhJOGVZdVciLCJtYWMiOiJmZGU0ZjU0NGMxMTg3OTllYjY5ZmZkNDZiMzRlM2NjNDY5M2M5YWE1NjNmZjIwNDMwNDEyZDIwYzg5YWJmMWJkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/correction.postCorrections.json b/tests/Fixtures/Saloon/correction.postCorrections.json new file mode 100644 index 0000000..5ef98b1 --- /dev/null +++ b/tests/Fixtures/Saloon/correction.postCorrections.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IkVDNElCaEFDZ1lFS0d2RWowUmpRZnc9PSIsInZhbHVlIjoiVTBHZW5nYUVmYW53b0taL3BnNms2N29qMFN0SVhqWUhnT05oUmpRYWxJRHM4eVhicG1YWDZUMUQzKzd0Um9udTRGdEhnUHhIb01RVi9uS3NSQ1B0Z084MHVWc3RyMjI1TlQveEtqeWZWakdXV0FKY1pURzZDdzdUMTZWWS9wWk4iLCJtYWMiOiJmMzlhMmI0YTNjODZlMzVlMDMwMGY4Yzg5ZWE0YjlmNDQ5ZTU0Y2VmYjUwNjU1OTM2NTFjNzEyNWQ0MWY0ZTI5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlBqd3VmckhqTXl3U0xwUEdqbGVCbEE9PSIsInZhbHVlIjoiU21VTVgvdUlwcUhGa3NKVXB6eEFaRm40N01mazd3ay9IRTlaS3piTzcyWjhnb1EwY3Ewc0diUVNDTUJ2VkV0ZjFZOTFQcFRXRnNpczJORnRGOWxramphSFJ2eEpmRHZPaUdWcW1IendEWUR6dGtyRG5VT3ZzTEsvU2pwTHZneXAiLCJtYWMiOiJjOThmMmFjNDdkNzI1OTZjNWQ5NjhmODRkYTJlYzNkNzViMDg2NmRlNzU2ZmM5YjZkNWQwNDlmY2QzNTg3ZTFiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.deleteCustomer.json b/tests/Fixtures/Saloon/customer.deleteCustomer.json new file mode 100644 index 0000000..5eea56b --- /dev/null +++ b/tests/Fixtures/Saloon/customer.deleteCustomer.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6InhDZ3VIQWRDZkgwbE1oSGNENk5IZVE9PSIsInZhbHVlIjoiZDhLRFZBNlhhU01UbGlPdG5rVGErQitWOHA3dTh2YmtwdDhWVFRZUkQ3eGdIYVIxeUhYdXNYWmw0Mm5NMDgrRFVwVFlsUGpTbGVvd1hweWsrN1pEMVF2bmRZdW9wejQvdURhbW5SVjJnR2dST1BMNnVnUlAwNmRkQUZvYXZHZ2EiLCJtYWMiOiI1NGMwZDgwYWUwNWU1NzBhMTk5YjE0NzU5ZTBlMzcwZTRiMWU2N2VhYjljNTZkZjhiY2RkZDZhOGZhNGU2ZTQ1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InBEYmU3c2ZYTGRWVGVXTnlFQlZ3UkE9PSIsInZhbHVlIjoibXJxcTdrNzc1emhRTFZnQlRWbloxeVZVSVBwMUpudk03NVA2QVBwTTZVd1NrY3E5N2x6RGVkSllsUVNpbmhXS2F5MytwSzFWcHZkbXQzelFUZndpVE81THp0aFl6c3lTbE9UMVVGdEY2NXhZZWg2NG8zUWxMdWs5Y1dWMlNOWHEiLCJtYWMiOiJlNzZjNzIyMjBmYTY4YTkzNjU0MmIzOWVhODFlMTE1YTIzYTFlODE1ZjhmMmE2NGJlOWQ0NTlhM2JkNDlmNDhhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.getCustomer.json b/tests/Fixtures/Saloon/customer.getCustomer.json new file mode 100644 index 0000000..3f793c7 --- /dev/null +++ b/tests/Fixtures/Saloon/customer.getCustomer.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImxzSWxYR0N6a21Rc3diSE5MazBvWFE9PSIsInZhbHVlIjoiSmxralp1ZVpTYnd3anUzaTBjVy9PajVsd0RMZERTZWlnSmxkNWhrM2tqRkx0b1dZYkY2WjJpcG9JZlJtTURHcGlKcTcrOTgvZFJZVnpGOWFmVjRpVEdlREwvdnV2U1d4S3dCUFFMYjJsSVZ1WlZyZjhocVhKOTZIRXM5TXVBRTUiLCJtYWMiOiIxYWQyMGM5MTZjNjEyNWE5NTNmOWE2MzJjY2RlOWYzMDBlMTcwZTdkN2EwMmU2YzA1MzVlYjFlN2FiOTA0OTk5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6ImJuYy9Sa0VtOUprUk1JaFo1alBudUE9PSIsInZhbHVlIjoiamlueUhlci95bnZvOTVJVU42SGNUQmtDemk1YUJvSEtuWDZ5NUNidFVhVkxSQ1M2TTd2NEVyUDBFanBaYTZpeTlKL3BBV3B4aTVLVVBRTGJ3WGNsUVpMaGZDQ3h5ckJKenIrekw3aEZFTVFhT3ZnTzRSdkxLUzlXdnNVSUpGOSsiLCJtYWMiOiI1YmZiN2ZiNjliMGQ4NTk5OWY3ZDNiYWI3ZTkxNmJjY2ZmOTdhYmJiNTMxYjc3M2M4ODBjMGZhMzYwYzQ3M2M3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.getCustomers.json b/tests/Fixtures/Saloon/customer.getCustomers.json new file mode 100644 index 0000000..eeaa63b --- /dev/null +++ b/tests/Fixtures/Saloon/customer.getCustomers.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IkFSeGoyWGYzd2xSb0R1MGlOcEh3Y3c9PSIsInZhbHVlIjoiaUFtWlBNemF3U21xaFFLT01FR3gyM0Q5MGJySGZJY3Rqa2ZtUzcxTVJpUit2Ny9XcytqV1YyOE8yZys4WnV5aXR5UDZtR1U5M2tZNnh5cUVrQnZDTnV0TVRKYXhnUkw3UE1SNmJsU0JWZnBuSEVtNE10NWROMDJMb0lGekJwLysiLCJtYWMiOiJkNDBjZTk0MjAzM2M0MmY4ZDQ4OTJiMDY0YmRhNmVjNWI0NmNmYjU1Y2E0MDI0MTE2M2M5NjM5NGNmMjZjZGNjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlhGZndDcnZmYXFRZWFjQnpkMFc0TFE9PSIsInZhbHVlIjoidmIvKzBrbzFCS01uTlZISUd1SDM2V0hwQlB0dUppZCsrTzEwaElKRWJGeTdpK21Fa1BGU3grZC9zQ2J2M1FQdFI3V1dtV0Rob20zaFNzREFrUnAwQ3NONHpOMkUyaG1scGc5dk5vSVhOUFNlV1ZBNkxnT0UvbEZsbUtydlVUZkMiLCJtYWMiOiJhOTBkYmIyYmYxNDJlZTlhOTQ1M2FhMzJhMDJmMGQzMjFmYjZmYjI3YWFlN2U2OGVmNGFkY2RiYmU3OTYxNmJkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.patchCustomer.json b/tests/Fixtures/Saloon/customer.patchCustomer.json new file mode 100644 index 0000000..43b2c76 --- /dev/null +++ b/tests/Fixtures/Saloon/customer.patchCustomer.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Ii9JMGR2NlF4QzYxSzhpQ2h2dFFrYWc9PSIsInZhbHVlIjoiN1VobEJCdDNLUzhXOEd6WFhhYjZzVWhSNFlWSGN3eGhoTVVGY3JxWVl2TzRmTEhUVHk1aWdDMUdFNUlTdXBSWmplc1ZhN2F5UkN0cFFoeVUrMDJiaFJnV0d4SndWZGRVL01XZ1B5N0c5N29yWGtWYUZJbCtuUEIyK0dvVDFhNmYiLCJtYWMiOiJmZGU2ODIyZThiZDRmOWQwODZkYjlmZjE4YjlhYjVkYzdjZmJjNDkxNDIyMzQ1ODdhMWZlNDQ2ZWY5MTQzZGYzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IllRajFHaWdJdGJvclNINXVqbDBGRWc9PSIsInZhbHVlIjoiVDYzTTNQaUdIamJ5M3J0MjZqUzY5ZkVlYy9BQXZKYVc3dGtQNk94TURobVU1eHprZGZ3UXBrRVNtV2dIcGx5Y2Rac2gzaTBvZ0hRY0t6Q29YK1Z3UDNqcGltQXUzRS9WOUk1ejJuSmtPMlZrUVVGaHFxL21SenhlWU5COFBmbGMiLCJtYWMiOiI3NDgzODkwNmY1MWY1Mzg4NTAxYTcyNmNkNWI0ZDExMjE5ZDQ5MjgyYmIzYTYzMmJmOWJmNzczNWMyYmRiYjM0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.postCustomers.json b/tests/Fixtures/Saloon/customer.postCustomers.json new file mode 100644 index 0000000..6c3b8c4 --- /dev/null +++ b/tests/Fixtures/Saloon/customer.postCustomers.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImJTaG40cCs0ekFZbEgrclVJUXhramc9PSIsInZhbHVlIjoiMXEzanBOeFQ1SE83NlRIcTlQMDBhWVNCTDlNSTBaeTlQdzlFUWZpRE9CdXpuUVBpOEcrQjJaNTV6ZFhiWTBDM29qeTBNMUhISU9ySW1HdjJXR3V4b3pGZC9jSFdXamlnUHdXS3hZZFFPcWhNanUva3pMUDd0U2pLZFBEWUo3U0UiLCJtYWMiOiJhMjExMzZjODc3ZTMwNzRjYzJmZGYwZGZkMzVmMGZjYTAzODdiM2I0Mzc1YzdiMjcwY2VkMTBiZDRhYzExMjQ0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InlxYjFwbjJyQjc0L3BSTjZ5djg5Vnc9PSIsInZhbHVlIjoicmNNbEp3RjluWHk3dll1cUFjUGVYaHpmaGFjczU4RFNMT2RwT0hPcURyVU5VZ3pIWlY2VUFLK0l5MmlQOUVnTmdpeThFMy8zbHRZaUZsRC8rWktYQTdQMjMyNVFtTHJIeW1EcGlvM0pjd2ZKMS9xZEVaTDNoc1pTUC8rMS9kNkwiLCJtYWMiOiI2OWMxYzA5MDNjZjA0M2YyNjIzZDVlZjMwMmZjMDk3NTM2OGNlNzM0ZDUwYzZkY2E0NmU1NDUzM2RhN2UyNzAyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json b/tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json new file mode 100644 index 0000000..3a183f7 --- /dev/null +++ b/tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImpRRVJ2ZjIwRXNvOWVuSERZWG5hMWc9PSIsInZhbHVlIjoiM3ZPVGtLYmJaVjFkenBZdUZjWTBIRkRLdFRDQ1ZsbGdtWExyZVZ4emtJUisyZWxHNW1PdjJaR0h0Q2k2NGNUbHQxTjYxbVcrSnRlbi9UVDNENTZsUzFJejloU2w4b3pSR1hoN0IrMzBXUHpLeEJqQW9rUlJCM2NWY2h3cFY2akgiLCJtYWMiOiJhNTgyNjc4YjNkM2FjYTdjY2QzODUxZTk2YThmYmUyM2YyNmQwYzdjOGY4OGI4YTQyOTNkOTFiN2FmMGQ4Mjc3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkpFdVpQWi95Si8rUlZTTkRmaDRwTlE9PSIsInZhbHVlIjoiZ2hBNUdOamNQYWpkaURxMWRlWkYxd3ZHTHI1dmpPc2ZCdTJxRVpOV0RqVXBwQXYxWExoL1V0WTBWUUtWMUxLdjBEWGVRV0VYdnFGdUdXbzM0aW8xUlV4clZyanZrUHd4Si80a1NiT3dvaU1mbXZYS2ptenYwZkxXb2RTRXNuMWQiLCJtYWMiOiI1MzdkZTk4OTdjNjI3ZmJhMDE3ZTlkMTM2MzFhNjU3MzhhZmJiYzI4OWE5YjA5Mzg3MGIyNDhiMmE3MWZjN2UzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json b/tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json new file mode 100644 index 0000000..fbf16c7 --- /dev/null +++ b/tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImdpcC91TzJkUWtRZ1JvS2hibUJQUWc9PSIsInZhbHVlIjoiYnBlSjVua2VzbTQybzFoSWxFS1Q0bllvNGtCR3VBYzlLVUxoekZ2cGlyTHNUNG9oZVhaVEFwYU9NOGY4UlZzZTBJaUxCU1A0OExHbnNTejNYTXhOQy9nK21KQWJBRjdKZ1A4NGh6V2t5bWlTaUxrTU1RSkozVlZDTGRBRFY3cXEiLCJtYWMiOiJmZDliYTFjYWYwY2Y0MGE3MzIxMWE4ZDVjZDgzMjdlMDIwYWQ2N2MwNjkwNjFkZDYxMWIzNjMwMzA1MDkxNzk1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IjVZQ245T3hReTBhWTlGUmJ3bmV6WkE9PSIsInZhbHVlIjoiS0txMXBhalBmY0tiYWRYMTU5Z25RTTBvZU9uMTNjeUFPK01RQzQ5TGFhMG5HYkY0WFFtL2J3aENZWWYyU1I1N0crZm5tUnA5dGg2Y20yMENndmxaaFpPL0EwOWlpY3BJZHBwNVdVTEh5MTd2cFhCYzh0UldXS1p4SzhYRUorVWoiLCJtYWMiOiJjZDkwNDZjNTU2YThhNDUyZjM3N2NhY2U4YmUwY2M5YzdhNmM2MzhkOWYyMTJmOTMzMTY1Yzk3NGFmMDQ0NDhiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.deleteEntry.json b/tests/Fixtures/Saloon/entry.deleteEntry.json new file mode 100644 index 0000000..25033e1 --- /dev/null +++ b/tests/Fixtures/Saloon/entry.deleteEntry.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Ijh5NnArcGxlejk3TC93NnVET1lValE9PSIsInZhbHVlIjoibnBscmZKWTlaU3hGODU1VTArMGhHeUxZcGlBSDBwZmZBaHBCR1lUOWsxNG5nM3FRWU50eU5VNHJObFd3OVNyUGdjNk5CTEIxaGM2enFKM1QxcFlmbFoyaVJ1UjBKM29Cd0UycHM3VXU2cStFb1hvdzdIVEV4L1RYSWVCRk5JSEwiLCJtYWMiOiI4MDMyM2I5N2Y0NjZiNjZmNzFhN2JiODQxYjE5NWI5NGQ4YWFkM2E2NTg1NDdhMGFiOWE5NWY3MTdiMWQ0MGNlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlZvNEVFSXFiUHNENVZaL2NWd1lib3c9PSIsInZhbHVlIjoib1l0NU5jcGNSeXh0cGgwZ3ZSS0lzVHhuLzlXMGpIU0NxNUltdGowR1J3cFZtN1V4SkI4eEhGeTJwRnl2WDMrNE85b2xGbTBDUlh3RnJZcjlveVE1VE9hUUl5U1NwTFN6TGFsTzRrbldoSXhoZ3p3NXo5YnU4RkpGclcvK1RFYmsiLCJtYWMiOiI4NTEyNTNmM2Q2ZmFkYWM4MjBjZDc1Y2M4ZjY3OGU1ZjFiNDExMjczMjk5MTAxZTVmYmUyZWYzMTBmMjA5ZGM0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.getEntries.json b/tests/Fixtures/Saloon/entry.getEntries.json new file mode 100644 index 0000000..e14c569 --- /dev/null +++ b/tests/Fixtures/Saloon/entry.getEntries.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjVFdVMxOWF6dkhzMG5YL3QrSTloS0E9PSIsInZhbHVlIjoiZW5HZ0trOXk1TDUyNm5QeXBLSEx5Uzc2Sjh0VTBMYzFvelV3WjMrYWw4TGJqYi8vVHV6eTFqYm1JRWVMZ05Tdm1zUFhGUTd1ME5PdEthejRUaWJlNGlJMEQ5V2xRMFdCVmdvRDJHVGNMdktMQzYzbzQ4WHAxdUtvR3NETzVCeEIiLCJtYWMiOiJkYmFiN2VlMTNiOGE0ZDNkZTZjN2FjOTgzZjk4MWY1MjBiNDUwZmY0NGFkZDY2ODEzMThmZTI2YzgxYWYyMTQ3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IklwSmtyRzlzUlhGNyt0OS9HSDcyM3c9PSIsInZhbHVlIjoia09rZTM3N25LeDNDNU5UdTNIZkRGb3RtTEtUaGJDeWprb3E1aTE2SVptZDV2NWo4VE5pc0VHMndnQ2phM0xJdjUrWStIejlLbG9TNWpPSVY4VUw1UFFKQ3hzUlczeFZYYkd5Wm1lYzlLcWZXNUl2aHdTdStOZVhDdVQ4YVVKY1UiLCJtYWMiOiI4OWY1ODU1Yzc0ZDkxNmM0NDk0OTQyNzIxZmQ4MmRiNTJiOWUzNmU4YmU4Y2NhMjQyZjgxMmFhMjg4YjM4ZDkxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.getEntry.json b/tests/Fixtures/Saloon/entry.getEntry.json new file mode 100644 index 0000000..604c8f8 --- /dev/null +++ b/tests/Fixtures/Saloon/entry.getEntry.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IllDSXMxRnZjZlRkMEZPUk5nNjlHVnc9PSIsInZhbHVlIjoiMmcvckNkMjVIYW54ZjBRUHlVbmJHdDlXNUxZbWdsbmpDNFpYT1hPaVdDMHVEcTR0YzM2Wm5ZS2JseUJsY1BUUU1nN1RMM0RJM0FCQlV4bVNTMlQySEMwdENYRmVRNHU4SHlpekcyNFRHRlYxY2lRUEVLTlVSZVpJV1NWSExLZ2YiLCJtYWMiOiI3NDU4MGE1MWFmNzNiMzVlODYzYjBjNDg3MTU0YTgxM2EyMjI4ZWVjNzI1ZDEzMzFjOTI3YWJjMTEwZGE5MjI2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IklveGQyK014U2FKWm41L0hxZHBiblE9PSIsInZhbHVlIjoiWis1WlB2WUFiR3VkZmZqcTZQRVA0K3Vmc2ZjSHIwTFlxd0tRRXZSVGZTT0lqY3IzTXpRRzhYU2haUWswbzJ5aWM4SzNJOHF1Vk9kK0kyVFNjK3UyZUY3WHk2ZDYzWmJkai9yN3piQkt6VTdrcjNnYlYvWkovbzgwR2JISVg5OG4iLCJtYWMiOiI4OTM5MzBhMmZhOTFmNDRmZWY0NjkxNzNhM2Q0NzljMzM2YjQ4NTNkZjlhZTZmZjdhMmQxMzZlOTY5MDBiNmVhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.patchEntry.json b/tests/Fixtures/Saloon/entry.patchEntry.json new file mode 100644 index 0000000..67f0882 --- /dev/null +++ b/tests/Fixtures/Saloon/entry.patchEntry.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlVkZEhDeFp1c0dQR1BmMXZLTnh0alE9PSIsInZhbHVlIjoiSlhCeGdUaHlzWDQ2ZjBUZ3FJWE5QSDlFNkFBdmxqWWcxRlQrNXhpbzF0aVRUaFFUMDJBV01BajQyWGV0TjlQOUxmaEc0dVpyWW1xZnY5U0JYeE85MEVyeUJ1S1hhZ25yRXd5YlJQR0UwUHZnNklqSkQ1ZU5ubU13S0NmcHM2MEsiLCJtYWMiOiI0MzRkZWNjMGMyMTc5YTQyZmRlNGZlZjhhNWU4MzMxY2UyNWZjZjJkMmJiMDE0MzBhYjk2ZDM3NmM5YjFiMjdjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IjNHRW8wWkwwTGMwUzBDWEpmTU5BM3c9PSIsInZhbHVlIjoiRURnZXBrMThJMm9ONFIxNGp5NlgvT0FFc3lobVdNaDZicTNKdnZZb2NTcFFCUE9DQlBiZzZQY1JZT3dpZ0hQWS90OU0rc3dIU3VwbVNSMUNiWFQ2OU05Y3lTcjBadVpXK3pqcFFNNGI5M3NoK0pjOXgrQlhxYXFhU1AweEtMbGwiLCJtYWMiOiI5ODQ4YzcxZTI5M2E1NWY2ZjAxNjU3M2Q5Y2FjNzAzYzdkOWM3NjExZjZkNWZhNThkN2FlNzY0NjlmZjhhNDUzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.postEntries.json b/tests/Fixtures/Saloon/entry.postEntries.json new file mode 100644 index 0000000..b703869 --- /dev/null +++ b/tests/Fixtures/Saloon/entry.postEntries.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjVKTDdLaFBKUmRDZ09PNVkyVzN1aGc9PSIsInZhbHVlIjoiU3hSTEljQ1hHMEZXOFdqZUlXb25xNHVCVFNDcGI2c2twait5ZHl1K0N4RjNONTZJNXJoYVNkcDRNRGxudTdBOUxrUFZvYXJCUG45VGdLK0MwamQ5WDlWL1FPNEZqVVYwV2Q4QzRvMEU5NjMzZU1XZ3FXRDEvNytnazVSMnNMY3oiLCJtYWMiOiI2NWY5ZGM3NGZhZDBkYTA1ZjBkZmNlODAyYTM5ZGFlZDI5ZDNmMjc2MmQ3NGQzYjRkYTlmZTIxN2JjODVlZmNiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6ImVNVnViMnJYY1pySnlmY05UOFVYYVE9PSIsInZhbHVlIjoiWWQ4U1hFcjlBOUxmaXFyNzY2cVVKVGZpbmlTMTNBQ0g0TzlIU1hsNkxnRlRlTmJEbEhVY1R4ZDhGVVYyUmltSTdvVm9teHdnZUNHN3NockZ3Rk03NXJ1RUZtUENub0hYTmpCUzBHL1ZTemdXU0V4a3JabG8yRy9sNFFMakJTWEEiLCJtYWMiOiJkZjcyNWVlMmY4MTQyOWRkNGE3Zjc4NjIwM2QzYmMzMzRhODRlYzBjNWUwYTZlMmQwZDdmNDJkMDA1YTIzMWQwIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json b/tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json new file mode 100644 index 0000000..e0fcd17 --- /dev/null +++ b/tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImpsN01WTlVrNFFmY2c2cjVxRTJOb3c9PSIsInZhbHVlIjoiMkZkUVkxMHVUa1ZNTzFJK0wxTFc2M1VHeGJpcm9JejQ0Slplb252YlB4MlN1TWxHbEYyS2tnb01rRmFpVk1tekRQWEc2bGxXS0tOQ09wbllkakIxNUxEM0RpRFR4SnQvb0FxUzI5WjJVMnAva3lkZER3c2hObGVLWU9UcVZHdSsiLCJtYWMiOiIxMzQ0NTBkNTUzMTllMWYwMzU0MzljYzY4YTgyMWEzMGE2ODQyNGRmMmYyODQ0MmVhYTllMWIzNWExZTJiMTdjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkpWVk11cW1IbjdHSXV3S3NCc3Y2eFE9PSIsInZhbHVlIjoiTnJ0Nmxva3FVVUlTSzFiL1RLM2ZnbkZxUWUyM0JhOWZ5d2h4TVFESXRTdjVNaURPbGJGL1JlYVZtSWMvTTdsdThSbEprZ3pJS3dWRlg2aXdCRWdNNXE4aFh0SUhmWUhMNUhwL0pvNE9KdkRPWjFtNi9jNVZzY0hqcWlCY2JZajQiLCJtYWMiOiIyOWY3ZjVkNzk5NWVmNzcwMGRlMjJlZTY0MmQwY2JjNWIwYzkxZDgyZWFlZWM5NGZmNjM5NGI1MjgyZDFhZGY1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json b/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json new file mode 100644 index 0000000..dbfe902 --- /dev/null +++ b/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjhSL2V6cjFZMU8xQit0ckgxeENReVE9PSIsInZhbHVlIjoiRk1Jc2E3OWJjMzl4dENDWStIbEcvWWdQQ0lPSXV0S1UyN3BQdUhLUUQwVVk5SERPNXljRkt2SnlHQ0lrZWI4bkNvVEVjdERmZ0tjNWdYVm82MHNtYU9Tck50YW53U2xPTG1tM1B4Nk5LVE0vaUZKdXBxcjdEa2dCSnpuVUx6eFMiLCJtYWMiOiI2MmQzOTRiNDJhODJiYzJlODNkMzAxN2E5MzQ3MWUxODdhMzNmZmFmZTczY2U5ZGZkZDU1MDJkODgyODNmMzgxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkdzSDA0NnErcDE5YUtDTkNDaGNTd1E9PSIsInZhbHVlIjoiQ1lDZGNxaFNjK0JOVkRjVzdpN3FubndqWWJacHpZNGUvelpmSE9HVHhhcGNNTVFkOXlwczRpM3BSdDJxbC9VZ0VvOWhndDNXMTl3THdkbzdYdkh4c3dLZ005UU8zejFlcmRWTU5pcDdWR0VzS3V4V0dxYW1MQ0JMRTZvQVAyNzciLCJtYWMiOiJhZDFkYWRlNWY4YmExYWFlOGE5MzNhOTcyYTQ5Y2Y3ZjlhZDJlMzc2OWVkMTliZmY0MGUzMTdhYWJkNzM5NGY3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json b/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json new file mode 100644 index 0000000..5e27f2e --- /dev/null +++ b/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IldvU0pONEhyblV6VE5mQ1hXd25kYUE9PSIsInZhbHVlIjoiV2ZtczJ4TDhETW45Y1gvTEVoR0RSeVFGQ1pxTHRFd2kzMUR3aW9JZW10OXlLNDdIVDdnQW9Jcm03dkRMVmU3ZCtFaW9jRFhraCtOcTlIQ2lnMDNiUmMvbi9qZUp3MytacFRIdWNiUWtsakU1MC82WGk0MFRSV1p6amNlQzlDS3kiLCJtYWMiOiIwMjk1NjBmNmE4MTdlM2NlOWY3MzJkYjVkMWQxNTcyNDg5Njk4Zjg2Nzg1YjRhMWZiMDM4MzI4NmY3MmExNmZkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Im9iT3hrMU1kZEJtVmRLOGpSbm9lNWc9PSIsInZhbHVlIjoiaGNpeDF6bUpkdDNFRE5yTFBvdUtnK0ZSVi9tQ0wwdm81NCt3M1Jyc3dEWCtyQXdvV2pCd0lCNGduMTYrVVZDVHF2aE1nWko4R1ZSc0owZmlWeGpMMExaeUQ5TEVQdjlkNGZEVHNwYkVoclhwVmlFRmJub2NKVCtPYXZUTCs3RmYiLCJtYWMiOiI4NTcwMDI1Y2M0ZmIyZWM5YTU4M2ZlZDI0YzAzYzJjM2FlYzZiNTY0NWFkMGY5NmM1N2Q3OWQ2OGY2NmRmNjEzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/event.postEvents.json b/tests/Fixtures/Saloon/event.postEvents.json new file mode 100644 index 0000000..3a3fb96 --- /dev/null +++ b/tests/Fixtures/Saloon/event.postEvents.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjRzNWtVd0VYQkphaDVHOUorZ3JxcGc9PSIsInZhbHVlIjoiUFg1ajZpZDZwR2kyNG16dGJlUE0zVlFFWDFOaWp5NHZ0SGRZT2p6UnhuYy9YQkhtV3Z0RlJaUUdmbTA4RnZQby9Na0VFckpiZlkwMnBObWw2SzVhcmRQOG9MSUorSkMwY2FHa1VMZVd0am14azc0aHhaNTd3T3lRbE5XdFhSQkQiLCJtYWMiOiJhYTUyNjdhMTZhMjg2MTBlNGVjMjYwYWE4YWU1NDRkMjk0Njk5N2Y4M2RjZGQxZjk5MmY4NGIxMWJkYzQ0YWIyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkkvaGExemQzd3VSU3pKSUpuaE1TSFE9PSIsInZhbHVlIjoiL3dqQVFqRVZDR0JiRlpZeXBDNHVSWGQ5bTI2bjZ5a25aVTFxYkI4UHlVZzBTSkFLN3RuWDg3VGpsMHV0ZWhhdFRHYnlxR1FpVUZBa2tmRk9QTkFCM0R0c3M1UGpoUDBDamRwbTBGUmRFY2VFelRXQXFYcnp1VEEyRVdiMUM3ZjUiLCJtYWMiOiJiNWNjMmNjNjVhZDM4Njc0OTg5M2E3NGI4ZTI1ODg0MTlmNTQ1YjE5YzZkMGU0ZmZhMzgwNDkyYzQyMTI1NTlmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json b/tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json new file mode 100644 index 0000000..d8d1d05 --- /dev/null +++ b/tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjlQQ2VuWkxSNWdiVE9xOEpwYTEvS3c9PSIsInZhbHVlIjoiZTNvMDBlcUZqQUVIZmlZMmhnUW9ISjg5QnVkY1c0OE9mdWgwc3UzMUlrZS9uUlpnNTFBNHA1a2o4RmllTk5ON21rMmZkOXcwSkYvazg3SG40Rnc2UEpESTd2RnZSUTl3cVMwTFUwMWRWWWlCRTRPUTdscXJPL3JSNnB6RDdmNHAiLCJtYWMiOiJiZDljY2ZiYTRmYzUxNGQ3MmJhMzJhN2E3YWU3N2M3ZTVmMmY2ZDFhYTI2YzcxMTM4OTQ5MzQ5OWUzZDljY2QzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkxhbG9OQ2t3LzR4S3RGYko1SS93Zmc9PSIsInZhbHVlIjoiM1lQY1pMZGdQN1JhVzYzRW9wY0hpTUw3azFabW1taWRyRk1yRUVUOHJ3VmtoK293QTJ3OXE3N0ZzenFGOWZyMGZBZHEyRmtRc21nRmpCNGxzVlNHYXJ0M1FZUXhDdWtxd2VucVhKMjRvMC9paExhajdidmJMUDFaUDUwS0NzeUciLCJtYWMiOiIyNTUzYTEwNzA2YWM0NTQ4MWJlNDQ0MDUxZDhjZDk4NjRmOWJjYWFiYTRhMTExMDBmMjkxNmJlNmZiNjRiYjk4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/incident.getIncident.json b/tests/Fixtures/Saloon/incident.getIncident.json new file mode 100644 index 0000000..27a270a --- /dev/null +++ b/tests/Fixtures/Saloon/incident.getIncident.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjAzNmxTVjVXM2psN3ltWXpZWUl5MkE9PSIsInZhbHVlIjoiQmptUk1jZmZBOGN2dmVlejd6N0QrMStNeDU1b3p2TjlBaUU1anlOMTRXcWVlSXJ2bE9IVEVyZkg4R2ZOOUxOZDJUSldFT2J3bXJRVFlpT2ttemYrc0JTOEw4SEdFNHVCMGErRU1CVDMxcXpNVi8wUWdOUXlFSWZ6SEQ4RkFnSnQiLCJtYWMiOiI0MzgxZTIzY2RmNDNiYTUzZmQzMzY3MWViM2U0YmRkMWNiM2YwN2QyNzg4YWMwNWEwNDlhMTUyNTIyZjc3YzY3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkU1WUF6b0NzTzZpS1FiMnVKVWZpdFE9PSIsInZhbHVlIjoiNkhqaWk1ZFFZeGZhOWRrNmpOdHhTaGZHQzlXT2J5ODlGaVFwSGh6UU5MRkZxeDA0cHFLMEc4QkZmcHZzUzlvRGs4ZWtNOHowTk44MmdjRy9oMzBTQlhvQUF2b29vN1J4Y1Z1TzVPK21lVmloTFhYV2pwdWZ3RXhQMTFkVkMwdnoiLCJtYWMiOiIyNWU5MGY1ZTM2NDRlOWFkNmVjM2YwYzlkOTk0ZGQ5ZGJlMTJiNGRmNjUzMTRhZGMwOTJiM2I3Y2YxNGIzMmEyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/incident.getIncidents.json b/tests/Fixtures/Saloon/incident.getIncidents.json new file mode 100644 index 0000000..59ade74 --- /dev/null +++ b/tests/Fixtures/Saloon/incident.getIncidents.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Im5lUWwyZk4rQzFMTW0vdUE1dEVtRUE9PSIsInZhbHVlIjoiYVhXN0tockhLamhkQ3VPWTZ6NkNSWm40YkYrT0ttQjZURUdZTFR0VHhkLzQycXIwZkNiK1d2eWw2L3dDRzhTQmV4V3hYWFFUelhmVmdaU2dMQS9UUHhlcFdHKzJMWlcybWJNanY3SHVQbkxndzAyei9YdHJnUjZtOEZWbkhiajAiLCJtYWMiOiIwZDYyZTdmY2QwOTA1MGJjNWE2ZjZhZmUzMWUwZTA0NjE0ODhhMmIxMzJiN2JiMjM2ZDYwYWM0ODA5OGE4NzEyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InRGVnJtRjVoOHNkcWNkYnQwNjV6UGc9PSIsInZhbHVlIjoiZ1EyQ05FYk5wTWRoZUZZOW5DQjl3em1NMGpxRno5aDdpeUhqam1hRUNTdXBrSFBSbHpFSGVDMlFueG1CUDFXY3lGWGtZNnBydHJUaE80UVhBSitPSzRuajV2NkN3Z1lsc2k5V3lqWUg1V3hodms0UG5tV2VUbUJJeGtzTSs2cFoiLCJtYWMiOiI4OWY5MDcyZWZmYTAwODZiZmVmNDBiMDJiOWUyYmRlMzU0YjIyOWMzODljODNkNTdiNTcxOGYwYzM0NTY0YzUyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json b/tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json new file mode 100644 index 0000000..41fc810 --- /dev/null +++ b/tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlRJUVlZWnRVbUVmaldCZWQraE43eEE9PSIsInZhbHVlIjoieVE1dWYwUEZ1MEJ6ZUxwd1E1SnpuTkoyK1Jadm0vbll0N1J2VGdUOUxWQVg0YmVKSU41THZ2VG94a3J3d2EwcUIvQlV0bVJpR2MrVDYwQkhnNVA0V3VMYktqd2pEZFFaWi8xeHNkUHVYdGl2cEMvaHJaT0lSU3dFaVYrNGltMloiLCJtYWMiOiJlMTg0M2EwZDZhYjQzZTU2MDM0ZWFmMjAxZTZmYTZjMzVkYTZhNmMyZjE1MzI2ZjAyODQyYmM2ZGYyYTNmODNiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6ImtGQW4vTzBrWllSQ2M4Q0R2b096ZEE9PSIsInZhbHVlIjoiTG5USUlIRFZRdEZzOGY3ZkI1dGwxTEdIWktDVjJQK21IMmNOaHBhUW1CNjNtVm5abW8rQzEvVzJ5b0JxUmRtdVJlZXVSUXp6RlE4RFRCMDVFSnZMakovYUc1NTdzK1lMalFpTFR4aksvUDJkL1JxZzFjWnlGUWcrVHc3QUZFVUMiLCJtYWMiOiIyYWNlYTc2NTk0MzFmY2I0MDA5YTlhMTRlNTliZTJlNTZkYTJkNzU1YzM1OTViOTAzMmE3ZTA5ZDIwNmM1MmU3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json b/tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json new file mode 100644 index 0000000..8f6e1c3 --- /dev/null +++ b/tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Ink2d0M4aWFHOHFmT1JyWDY4L3k3RFE9PSIsInZhbHVlIjoiS2V0cU95bkV2cTNGR21ZOWlyRjdqSTNUVkJza3RYeldvWXUwVU45TnJKbE9Ta3JRbmtFbG00Zm9XcUVlVHUrUERjWjdSdlFPbHpUdExaQnArQ3A4TXc3REJTVmdwT3dqQktTQ2hFelhkSmRVM3pndGExVFBtNy9UbHp2K2ZjTXkiLCJtYWMiOiI0ZDVhNzkxOGNiNDNmMTQ0ZjFkZGVlYWYzM2YwOTlmODg1Y2QxZjQwNTU4OTE0YTA5YjFhYWRiNTU3YTA4ZjRlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkxFNzM2V0NkRHRTMVFkZURWbnc5dHc9PSIsInZhbHVlIjoiNTlyamhHSXloN0FXWWQzOUpUYTFUank0dmxaN3V4cXV3S3JCUHZzVnZOdS94Q29qWDVWU1hQQVdYd2lEYkxiMUI1WHczeStQMWYwWVV3VWZUY0o0R1FqSFcrelRZd2tpdVViSHg5L29tUGR6c2NlMHQ0T29GMkFZdVZQZ3pRZVciLCJtYWMiOiJmNTNmMzcyMjRjOTY4ZWNlMjg3OTUxNTZjZThjZGY1YWU4ODUzMmJhOWQwODY5YzI1M2JkY2ZkODg4MjJhMTg4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/me.getMes.json b/tests/Fixtures/Saloon/me.getMes.json new file mode 100644 index 0000000..cffe264 --- /dev/null +++ b/tests/Fixtures/Saloon/me.getMes.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6InJEeFpzdmgvc1VjL1BBT0xuVmlOeFE9PSIsInZhbHVlIjoiVzRtL2FGZE16U3RrVE5vZ2oxOVdRUFlFQTZvVXNOb2VtdEVSMXU4V0hNaW96TUJNRmVJVTJoNnBsKzlrZWxwUnJtZnJVZmxJOTBmZlNlRllROWxjRjJ6N0ZMTUljRXl6OXRxVUdobHQrY0c2bzIzVUFDV0RLRnZEcnBpRFRGSmIiLCJtYWMiOiJlMDg2MjI0YzRlZWUzNmJlNGNkM2FjODQyYzZkNWRhOWMyOThhMmM4YmFlMjliMmIwYThiNTRlNDcwMGUwNzJmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkFsRjhZMkxtUEhsU0FTZnIwN09NMEE9PSIsInZhbHVlIjoiQkRXeE85SGhBeVNXWW1Qb3hxbUN4NzI5V0VyeWt2UkJaQ3k1ZU5QSkNOMDRlUXhHaGpTUFJFNXFaVHlFUnJEOHp3dTAvc3o2RTBCY0R4UjdkdDIrUGM4OG43VXF1dExvME5wc1I1Q3lPcXhRRUdENkFiUzlzRitkT3V6MEVPZHoiLCJtYWMiOiJkYTU1YTJlODczNGVkZWU2NzY4Y2I5YWFmYjE2NWE2MGNkNWM3MmZiOTFmNmM4Y2M2ZjhmMjEwNzgzNzgxNTQzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/number.getIncidentsNumber.json b/tests/Fixtures/Saloon/number.getIncidentsNumber.json new file mode 100644 index 0000000..e471153 --- /dev/null +++ b/tests/Fixtures/Saloon/number.getIncidentsNumber.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:16 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlpPY3doOUxIb1d4Vm1MMEZCSnFTdnc9PSIsInZhbHVlIjoibm9sVHBKMzJ0S2xwbjV3Q0FwenUyOWd6K3RlbGQvRk1JL2RUME9xTUhXT1NRUmtST3hKV2hPTnRkNkR1SUpiK0ErU0U5TGNIWGhhY1RLd1ZpTjN2dEVvMXNXcHdQd3d2RzM5TkE2THVkZWVvOWt4aGVSQnkzTFhXU3VMUW9BRVgiLCJtYWMiOiI0YmRlMDQzMWE0YjdiOTFhZjRkMmViMzAwOTJhMzkzOWMzZjQ0OGFiNzcxMDUyM2Q5YTFkOGRjMDQ3ZDE3M2EyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InQ2L1dRQjVRMDBXaUM2N1NLblZpcUE9PSIsInZhbHVlIjoiVkZpdG1hNlh1S2tDUGhDcWpkTURXTWNReTEvNStrZGhpQkV5QTJhUE5adytMTHdBQUoxaUZPdU5MMC8vZGl0ai8wL0JxOVZNeTZ0RjUzRUg0V0x3ZmM2bEw0TWR4WDEyVWNsdEcxQS9vYlFlTDhyaStSSENIUWxUMnhKNkZLYk4iLCJtYWMiOiIzZjI1ZGM3OThhNDEwNDk3MmRlY2NlODFjOTc5NTVhODJhNmQ1NjVkMzgxMDBmYTZhZGVkYzFkMGZhMDBiZTg0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/overtime.getOvertimes.json b/tests/Fixtures/Saloon/overtime.getOvertimes.json new file mode 100644 index 0000000..cd9923e --- /dev/null +++ b/tests/Fixtures/Saloon/overtime.getOvertimes.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6Ik5ia3pQa2NmMDFHSVJSbjBXZWIxK0E9PSIsInZhbHVlIjoicm92bEdXWkd4RjRvT3FVekVrQnNWSnVsL05JWmN3SFpoMFEzdXpMRC8yK0JicUg3RWRKSEhsdkVlUEV5TVk1S3VRQk5TdFJJZW11SkU2NnIrR0tqcm9LOWM3Q2FtWm9abXRxaFZpK0lKbm5NYndXUlVnWVhvc2xLdXoyczFZanUiLCJtYWMiOiI3OGQ4MjhjNzk4NWQ1NzhhNGQ5M2I1YmUzOGQ4YmNlOWMwOTI3ZDk0N2MzNzgxMWE1NjVhZWQ3MjFmNmQ0ODc4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlY4cTNEWCt5b28rYXNEWXZCTWJvMVE9PSIsInZhbHVlIjoiVkFuNjhwQXhwaHdlbDY4Z2oxRzBnVWVuSi9qTGdaSWdDRkZEM0IzWEszV0lwVm90NUFFY2w5NUVFcXU5T0trajd6UEUyUUpHY01DdmZXWWhMcDEzYk5vbnR6R0FTRXpuUkRqN0N6V3M5eGJMNzc5V015MWlIUXRBd3luQ3JwSUwiLCJtYWMiOiI2YTBmMTM0MTQ2NGU3ZGU3MGRlZTFlZTA2YTQyZDM1MDA4ZmNkNWZlMzFmYjA2Mzk1N2RjOTY1NjYzYjdkNTlkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/period.getBudgetPeriods.json b/tests/Fixtures/Saloon/period.getBudgetPeriods.json new file mode 100644 index 0000000..ce07d96 --- /dev/null +++ b/tests/Fixtures/Saloon/period.getBudgetPeriods.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImpMSkR4WWJJeDNhakhvdUJLSDg0MlE9PSIsInZhbHVlIjoiaElHOHJWK0wwR1V4ODkrWklucmhPVnoxcUZENkJlWkVTSjFlMThKZGVJVlQ3S1lMUWh5c2pEQ2ZvdlNNTnYzMmpQMFlqZFRCdy8xNEVnY2RDRFlsRzcyalJRYmhKZUFEaU5MdkJpbFJkMkNYcU9oM09UcTdtUlFQOTI5dXNVa0MiLCJtYWMiOiI2MDEwYzAwNTA2NDE2MjE1ODZkZjkyMjgxMmQ3N2U4ZWNhYTE4ODQ4ZTE3Zjg3ZTYyZGZmMTc1MzY5MjNhODJkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlNmRVM3ZzB3RFpXdTFSSUJtcXJhQ3c9PSIsInZhbHVlIjoiYzg5SW81V3hoVDB1VE44UVhDMzQ2WUVWVTRkUldYN3AxbmhwVFpZNnFGM3RCeUdqODhHaVV2NENzVUJvSXh1eTJTendDV0swVGEwTlNYQzR2Mm5PK2tPQjhUcGRXYllMTnpmczBhdkowRFhUVk5hODdSa1hTdFJSWVJCaU1tZzAiLCJtYWMiOiIzYzVlNGI1NTEwOWU5MGNhNDNkM2ZjNzA2ZjBkNzliYmVjNGI1NGYwMmM0NTlkNGJmYzYzZGRmMTViYTIyYWZmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.deleteTeam.json b/tests/Fixtures/Saloon/team.deleteTeam.json new file mode 100644 index 0000000..9b81cb4 --- /dev/null +++ b/tests/Fixtures/Saloon/team.deleteTeam.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImhIcFNxcE8vdVl3c21VYWs3NmU3ZFE9PSIsInZhbHVlIjoiaUNEMlVSUmNza3NHb0dGQ0c0MlRNM3Bjd0pKTVVrZzN4Z0JrSzVUQWZWb2pTYnZhTVREWWxsTjR0bjYvYzVkKzdRcWJQcUtZZzVlcGdLN0V4VG9BL0VUODRRMW41UEdFbVRRMmw2MUdXRXFLdlpiWGFLQ2FSRUUxVzUrQ3pwWEEiLCJtYWMiOiI4YTU3ZmVmYmFmZDFkNmExYzU5YzEwZjE5ZGUwNmNmYTI0ZDRkMGI0NjgzYTQzNjNhYmQxYjI0OTM2OTMxYjQxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IkptdlRvbkpLT1g0NkNiZGZhM3lmSFE9PSIsInZhbHVlIjoicVQ0NExScTFFL3M3cnZVWnpOcmdmVzFjdkZsS1ZMMnFyS3pVeXl1Qm9JWlhqTEMyUTdueVlYMEQ4eW8wdUxqRlEzbWh6V0VJdHJYVHJsemRURG1meTRXTVNEUlNCcHA4MzFYZkRJS3ppc2h4WVNEblNlSXlFOFhOREZWRmoyM3giLCJtYWMiOiJhMTU3MDE4ZDI3YTE3Yzc1NWI1MzY1Zjk3YTQ4Zjk1Y2NlZDVhY2Q0NTA5OTI1MDhiMzM3MzMwOWJkYzE0MTlkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.getTeam.json b/tests/Fixtures/Saloon/team.getTeam.json new file mode 100644 index 0000000..8abbb57 --- /dev/null +++ b/tests/Fixtures/Saloon/team.getTeam.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6InV4Rk15enM0VlNFQ01sZXUxcVJMU2c9PSIsInZhbHVlIjoibERyZXZ5YkVMNHpvZDJ2eTdhZ3RlYUlHM1BhMUQ4bWtuR1JaRVNmb2lXUmt2Tjh3UVRHcmVzNjI5VXQvQVBsN0NWRlFIQ1pZL2RmOVA2VzlhdjZYdHJ3M3YxeEJVOTREZlc1TUpHMkF0b0djYW9tZFhzL0xlRzRMYTJtNys4V3QiLCJtYWMiOiJhODdhOGE2Mjc3YTJiY2U2Y2U2MTExOGNlMGI3YzhjODZhMjAyNTJkMmY4NzhiMjUyNzdlMmJkMzNlMzkxMTM4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InFkNDFEQk5ZZVUwR0RqZXpGMzZnMVE9PSIsInZhbHVlIjoiek1qY2ZqbTRwdDVoN0pCcW9mckVpb0ZHTzJod3lpTFJpSHN6NG8vMGJDRG1CMlJjc3prdHdTYmRZemtQa3dtU2xzUFd3TVB0YzUyajRwb09uTWlHSFFMU1NiaVk4S3padE5tU28zWk9GUmdsdXRKd1QzeGY4Mmsra2U3NnNYZ2ciLCJtYWMiOiJlZTIwMzdiN2NiYTIwN2U1ZTZlMTUyYTNjNjZjZTdhNTVlMDFiOWRiNTU1YmUzNDc2ODk4MmMzNDBkMzA2ZDBlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.getTeams.json b/tests/Fixtures/Saloon/team.getTeams.json new file mode 100644 index 0000000..f5cb52c --- /dev/null +++ b/tests/Fixtures/Saloon/team.getTeams.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6InJjcjh4bHkrZm1Na29HWWw0ZUJEakE9PSIsInZhbHVlIjoidS9zRVRLSm9CallLZkR3dE4vbnZjK0I3ZzRJT3IzZGozdXpnL0IrUDVBc3FEek1kNFZqOG4yZFhzN1hmL3dYalFsVXFQNU11aTU1K2V2b0d5ZWlZTUt6YTdMQnozWHJYWFdtMGdQREFreHJBK1dNN2JEdW5HcFA2OFVhTTcwR0MiLCJtYWMiOiJmODYyY2QzNDAzOGRhY2VhMjQ0MDMzZGZhZDZlYWQ1Y2ZhNzU2NDlkODg3NzYzYTFhNjYzZjk1YmEyN2YwMTQxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlB5SHZadXY3UzRFZWFqdmRBM2NlZFE9PSIsInZhbHVlIjoidllyS1JYNlZkaG5LeW9ZOWdlcWlESGh3RjlTSEZWYlhldzBwZUd3RHVuNzY0RFFtNkxpRnVsMmZPbjZTK0Z5L0IxNWFsOFVCdDY4ODY3WU1aNzc4MlhWZEtZYWZHa2VBL3VoUVVmQTJmbnRxdzlMK3B3Z1pmanEydCtzcXMyM04iLCJtYWMiOiJiZTRmYzM5ZWQ2OTFhMGVmMzg3ZTlhZWEyYzhlMzU4OGVhZGY4MDU2MTdjYzEwMDg2YzcwYmU4Y2E5OWM2YTk1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.patchTeam.json b/tests/Fixtures/Saloon/team.patchTeam.json new file mode 100644 index 0000000..9995d8e --- /dev/null +++ b/tests/Fixtures/Saloon/team.patchTeam.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImtWbG5UQSt4bkQvTU1GaUM2Wng5Nnc9PSIsInZhbHVlIjoibkZndVZhQSt5Smwra2pidDA0aCtITjF2RStaZjlDT1dtU0ExTVg3UkQ3dTNncytPM3dvUEhvbUpVTVAwS0Y4WFI3M0tjNGV2alFhRTBvYnNCNkl3cE90RFNPOHFzMTVVUUc2MUdRRGU0ekFtMTFDTGVua3NYUEpZQ3pjTlZxbDMiLCJtYWMiOiJjNWViNDE0OTg4ODUzODcxMzJlYzU3OTY4NDA4ZDZjZTZlMGJhYTg0ZWE1NWZlMDg0ZGUzZjVlMWJjZjM3ZjVmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InZVZ2t3OUk5M3ZLdldkWjRZblRKTkE9PSIsInZhbHVlIjoiY1F0aG8yOWF3OHI1ekhVTWU1QmlocUg1Zy9zNk12Q1ZPbUtQVFZmbkovU25GQTRNUndzSFdSZENBL0FTb0dLNnkzREtobER0UzBvb2dqRmVXbEFPSTBSeWhXUjB2YkRrdzFQU1lBd1krK1d3RzFUNWM3Q0Q4VFRFT0lQb3d5ZmEiLCJtYWMiOiIxZmM4YjEzYzQ1Njg0YTY1MDI5YzFmMjE4Y2RhMDY0YmU2YzU4ZmRmZGYxOTNiMDg2MWQ5ZDFkMTdjNWRiMTZhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.postTeams.json b/tests/Fixtures/Saloon/team.postTeams.json new file mode 100644 index 0000000..f979dbd --- /dev/null +++ b/tests/Fixtures/Saloon/team.postTeams.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImQvV1lpdFlHUkFhYU9hbS9XczE3VlE9PSIsInZhbHVlIjoia2J2SWFRQ3piT0pNZ0YrakllaFhkeGhjQ2tzOFdjQ0pHZXJ2TnVQVkRDeFJ4cFMwR1IraDNYemFaT2NmRkp6MkVSZE1SaTVuVEpIbi9VSEVEQ3Q5bStQb0VzL25EQ3pGTGFZVTUzcEZadVNDMldoUHRiUDRQb0lsT2U5QklhZkwiLCJtYWMiOiJiZjA5MTZhM2Y4MjZhMmNjZTY1NTZlYjEwNThlZjliYThjNzBkMjg0YmE2OTk4ODUxNjM0MDA0NWQ1ZjYzYmEyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6ImVTWGViR0hWSmU0U2pHMHA5cy9JcGc9PSIsInZhbHVlIjoiWWV3d3c2bk53aUg2TzBpaERRN1RYZFpTQXRiZ3RrUUhSZU54OU03QWFMWWNXV3YrR1Q2enMzM2w1bVgwVTBIK05Qc2FaMEVFVEhKT0FOcEVwVW1CcURtUHlhNlJ0Y2JXQy9aRkVKSkY3TjFkeDFyUHBES2VZaFhtSTR6aEFhNkIiLCJtYWMiOiJjNmJmYWM5YzNjN2I3OTY1M2RlMzUyNTRkNDhlMjdiN2NmMDU5ZTU4OWU2NjdmMGZjZjlkY2Y0YTc1YWY5NjE3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json b/tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json new file mode 100644 index 0000000..c8ddfed --- /dev/null +++ b/tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6ImFxK0ZNSE85aFBna1VERGtCeGFmb1E9PSIsInZhbHVlIjoiakNlZVlpK3cxOUM4Z1FWKzZRNm1zZ0k1Zndad1EwRzZaeDROQkt6YUk5VVpuREp3MHBXVHFSd000a0ZTc3R4anJWNVg2YkE0UnhCby9CNWFTZk42dytoaGZLYk5PSmI5Z1RNNHZ6RDN0NnBiR3B1Rm5WeEdhQWVvekNqUkFTdzkiLCJtYWMiOiJjZGZmMjljMWQzM2I4M2Q5YmI0NjA0MzI3MWIzYmQzZjQ0YTE3MTIzODM3ZGYyMGY3NmQxZDJiMjFmNTY5MmFkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Ikt5VmtFSjdqbVdMU2hac1hRaWw1K0E9PSIsInZhbHVlIjoiTTBVQ3FHSTJsU3d6Rm5QeVdVUGVLdGdleXM5M2Z3cUVpRXBjcEU4RmJWMy9lS2FoMDJsMDYxTUlVNXUwb3JPUmxST2FYaEtUWkJ0QnZrdVhRZ1RmRm93Vzd6aWN0MlRMemhYaVlnYmVKeEMrVEV1QXFBZWRFdlFrejdacytXMkoiLCJtYWMiOiIzYmNkZWVjYmRmZWMzYTc5MWI5OGIwNzk2ODg0N2I5MjgzZjFkMTlmYTI0MWQ0YTkxM2VlZWFjMjA1YzY3NWVhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.deleteUser.json b/tests/Fixtures/Saloon/user.deleteUser.json new file mode 100644 index 0000000..785982d --- /dev/null +++ b/tests/Fixtures/Saloon/user.deleteUser.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjVyYkJmZHRWZG43WXFpbklhMHFSM1E9PSIsInZhbHVlIjoiZjZoVDRKcVI0Q1lSWU51OUUwa1NyOWk3Z0VsYUVQa2dodTh1S3FYRjFwaDc3OHRzVFR1Nm1nUXNnVzI1eHVVRzVDRkpZaXVjUWVDS051TGU5cDFmRDFVeFpQdGZEQ3FXRmhXVnhpQWNoWU43ZmZ0cUZ3RGJpd0wwUWdFeTlMbWgiLCJtYWMiOiI0ZWIzZTNkMzA5OTM0ZGU4YzYxOTMzMjE3ZDNiZWVmMTQ3ZGUxMWQxZjIyZWJkZWRlNTQ4YjYzY2FhMGFlNTRmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IjFUZzRYSllLMG13VkJpUUR5OTBzNmc9PSIsInZhbHVlIjoicFNETjRRaDdpL3djVFVVQ2RXOU1ab3h5czNoM1V6ODZmZzR3R2JnMzA0ZXFPT1NrS2VOVkpTcjlIclBJT25mVU9FLzVGY0dWMVQ3SGYrb0V0WW92ZFM1b2lSMjh3QWxoZEQwUDM1bHZHeXF6V0YyUERJdEV3Y1ZkdUVjbFY1a2oiLCJtYWMiOiI0NmJkZmI4ZmRlNmY3OTBhZjA0ODY3NzMwZGQ4ODdlMmM0OGY4N2Q2YWJiOTQyMDIzMTA4N2I2MTQ4YTA4YTIyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.getUser.json b/tests/Fixtures/Saloon/user.getUser.json new file mode 100644 index 0000000..8774f43 --- /dev/null +++ b/tests/Fixtures/Saloon/user.getUser.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6InVEek1Qc0dheGpVS2svR3BpelZBNHc9PSIsInZhbHVlIjoiazVuRkVhaU9GS3JRNkJrbk5rQi9VTi83bTh1OVJUNlFSVEpsYnJ1cWFVbGgyblZvRUcrdkRsTTBlS1pWZ2pma2tmYXVWdmRzY1BvcDFodXRIY2FGaTJHMlZGMzZyb21EN0FxS3lVcDVSTTUvMHVXNWlWTThEU2lVZXd0UElKcm4iLCJtYWMiOiI5YTAwNTU3MmEzZjg0NWU0NTIxNmY0MGE0MzEzNDBhNGJlOGFjMDkyMTI5YmU4YTQ2ZmMxMDE4ODY3NDQzMTI5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IlBwSE94YlBHK1RrZWJBaWtIZWdzNGc9PSIsInZhbHVlIjoiR1llbGhUdXkyQVFDc04vOCtMUzZ0MC9PcXVwWWp2REtYb0p3V0JIRW9ZazNBYktNNWw3MDRzeTZwelJKOTVoZ2EyVGVrV3dQaHlnU2JoNjRsK2JxUTA1NmxrelFpRGdXb0hZOXVadFVHRjFkY1ZMZkxobTFVTUN4MHRTL09BMm8iLCJtYWMiOiIzNmMzMTQ0OWU0N2FlNjU0Zjc4MGI0ZDZkNjkxNjg3MjAwNDliMzczMWExNDBiYzY4OWQxZWUxMGMxOWVkOTQxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.getUsers.json b/tests/Fixtures/Saloon/user.getUsers.json new file mode 100644 index 0000000..d0ddfb5 --- /dev/null +++ b/tests/Fixtures/Saloon/user.getUsers.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IkNzOUs1UmhhbE5vTFYycDNjcUdnVHc9PSIsInZhbHVlIjoiZXY4SnJpM2VzckZPd2F6akVEcWQ3OXJxVGV4MURHd1Iwakl4ZlA2ZTk3ZDFBYkpZQ3RnanVPVTJjcDlkay90ZjBVV3BZTE94di9rUmF1NC9McVZwdGZpUnJnSGpQSVZ2R2dUckZYNmRZalEyVXQxSjFzaU5IRWxTeEhIRDFGMTkiLCJtYWMiOiI3MTExMTBjNTc5YWQwMzc3NjM2MjFhMjM4NmY5ZDY1YjFkMTA2ZmUwM2UxNTI2ZjkzZTUyN2EwNjAyMGM4ODA2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Ik50bHA5SmI4Y3ZCTllnV0Yvcy96RlE9PSIsInZhbHVlIjoiMFI5bUliTmErL3hCNnlBUHQ2V2l3VWdSVzcrbHBIV0tvZlRWcTMyL0xRKytKa3U2dS9FR2Raekl6YWtxZ3lEZnhLWEZhZzY5SmZjSXptcDd4UkdWU0xaNzErQ1U4UFkyWnpBUmZIbTZOS0xoWk81eDFzelFJYmFPcCt3d3UveWkiLCJtYWMiOiI4Nzg4YzNkNTk2ZWRlYWY2N2RlN2Y5MThiOGM5ZGRiYTZmNGJlMzI0MTE2ZTE1Y2VkODM0YjYwMTgwNmU1YThkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.patchUser.json b/tests/Fixtures/Saloon/user.patchUser.json new file mode 100644 index 0000000..c3ee418 --- /dev/null +++ b/tests/Fixtures/Saloon/user.patchUser.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IlF0V04vR0pFN2ZFYmVYMFFlZ2xJd1E9PSIsInZhbHVlIjoiamFZdjBNblVnTjlWYmpUWjJmQUk0ZE9RNDZRWjUwQURFd3hmUldiZEVnZmZLU3dZTTJWMDZ3akF1UUh5MXZTNGpjcU1zU205RUJ1SXV4cGUxajJNd3hXa1lUWUVEa2hLUWFtWXVreVR4ZnpUVGc0UmVjWTZLM21CRjhqSG5vdkIiLCJtYWMiOiIwYmExMDZiMzBmMzBmNTY5MDc0MWQ1M2U1M2I1NzQzMjdiODgzN2MyZDA4OTA1ZTRlNjNlM2RiNTIzNjI3Y2FjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6Ilc4UjZEMGRqbU4vOXMxbkJGZEk0S1E9PSIsInZhbHVlIjoiK0RXQURRRnNHemY1OGpsdzhnekExSnlhQUYzbWx4cUd1NTB0Ylo0UXNuUEY0YWZMQXRlMnBJMVdidm8yZk1BYUVOSFdhaS81VlFEenhXclhzSFhuREROeHh0bGJpdHAzamtKWTZORC9YMVJqUjBxaW5sK2hBQXBMYmp2c0dBcnIiLCJtYWMiOiIxZDA0YTI5MjNhMzBmYjQyYjQ3MWNkNjI2Mzg3NDA4MjlhMmFkNGVhMzBjOTY5NDdkODM4YzJhNDZiYmQ0Yjk2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.postUsers.json b/tests/Fixtures/Saloon/user.postUsers.json new file mode 100644 index 0000000..388db2c --- /dev/null +++ b/tests/Fixtures/Saloon/user.postUsers.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:22:18 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6IjU3ZDhYUnZRcmlVRytDTXozOWJZb0E9PSIsInZhbHVlIjoiTUQyY0tlZFQxQWtSMEJJa3ZtdG00Q2xpWXZNWDBuYVRLeWxnYTN0S0FjYUY2UG4yNGlIMWhWVmYxeGUrcHVSRmUzdFAxUUx0alpOZ3l0S2gxcWsrNFRDK0o5MExDTUxHSjFDbVpUejlLVzlHbWp5MDhpQURRVTlLT3FhaFNEWmMiLCJtYWMiOiJiZjMwOWRhNjRjMDcxYzcyY2IzMGNkNzg2ZDE1MWFiYTA2N2RhY2IyNmRiMWRjNDA5MGYwMGZlZTgyZGNjYzNhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6IllVcmRhRW95OUMxdEtOQTZHa041RWc9PSIsInZhbHVlIjoid1g0YkN4OWF4RWJqdUhkTFhYb1JjMitSMjQzc1o4UXNmckhYU3ZacmJmY0Z1dTFzQ3kycUlSMm55TVlhUTBvTHVwM0VMOHZtdEN3cWtLYTByd3pGZlZjUnpNRE0rK2ZZZEJIL0pHRCtHSTBuN0EyVTlFaEFvelZXMWxiNitmYUMiLCJtYWMiOiJjNjgyYzY2NzVjNDE2MTRiMTdhN2NmOWMxMzk5NGJkMWYzMTY3ZWRjMzBlNDVhOGMxY2YyZDlmNTAyNGMzYjMwIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json b/tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json new file mode 100644 index 0000000..59bdc28 --- /dev/null +++ b/tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json @@ -0,0 +1,20 @@ +{ + "statusCode": 401, + "headers": { + "Server": "nginx\/1.25.4", + "Content-Type": "application\/json", + "Transfer-Encoding": "chunked", + "Connection": "keep-alive", + "X-Powered-By": "PHP\/8.4.14", + "Cache-Control": "no-cache, private", + "Date": "Mon, 17 Nov 2025 21:21:17 GMT", + "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", + "Access-Control-Allow-Credentials": "true", + "Set-Cookie": [ + "XSRF-TOKEN=eyJpdiI6InVZRmZkczN6cE52S1hxdGg2d2s3cGc9PSIsInZhbHVlIjoiaHhtdUJzOHg1OU0rMTA0VFFTV0M4YUZsRFVjdEhCQlJDV0ZpSDVHcFNHcjFzblhTcS9Hd0Q2eXhGSEQ1TVBqRXBTRmF0VG14VG9UMlY4Wmg4UWF0MVh2WXVSbjI4S0FNVGlBdHppMkpnU0RXK25tSzlkNlYrNHFtczlmcjYweXMiLCJtYWMiOiIzYmFjMGVhMDA3Y2MyZjY4ODY1ZGI1MmJlNGIxMzQ0YTI3NWZmMjZmZGFiZWFjMDhjYWVhYjc0OWViOThjNjA0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", + "timatic_api_session=eyJpdiI6InNENnM5Z2VLZm1QcmxOYlZjNis5VWc9PSIsInZhbHVlIjoiWDlNeW12Tkk4dEk0YWw1Q0QrNHB0ZFgvRlRnTkEwS051MjJWdkIxVUpTK3dsSGZ5eTdDckpac3hrWEhhWmRhY3Q3TmkrRUwzdXJUWmhlUU9oaGYyUmczRTNJakJMYmpYZmhpcE4vUVBnUzJGdGFBYTZqc0VMMHYvRHcvMDA0RkciLCJtYWMiOiJkOGM5OWYwYmI3YmFmMTdmNzI5ZWUyNzE0OWI0NjBiOGQwMjNkZTQ0ZGFlMWMwNjI0YmE2MzdmN2E3YzdjMjIxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" + ] + }, + "data": "{\"message\":\"Unauthenticated.\"}", + "context": [] +} \ No newline at end of file diff --git a/tests/IncidentTest.php b/tests/IncidentTest.php new file mode 100644 index 0000000..ac9af4e --- /dev/null +++ b/tests/IncidentTest.php @@ -0,0 +1,40 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getIncident method in the Incident resource', function () { + Saloon::fake([ + GetIncidentRequest::class => MockResponse::fixture('incident.getIncident'), + ]); + + $response = $this->timaticConnector->incident()->getIncident( + incident: 'test string' + ); + + Saloon::assertSent(GetIncidentRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getIncidents method in the Incident resource', function () { + Saloon::fake([ + GetIncidentsRequest::class => MockResponse::fixture('incident.getIncidents'), + ]); + + $response = $this->timaticConnector->incident()->getIncidents( + + ); + + Saloon::assertSent(GetIncidentsRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/JsonApiBodyTest.php b/tests/JsonApiBodyTest.php deleted file mode 100644 index 6834671..0000000 --- a/tests/JsonApiBodyTest.php +++ /dev/null @@ -1,79 +0,0 @@ - MockResponse::make(status: 201), - ]); - - // Create a Budget model with some data - $budget = new Budget([ - 'title' => 'Q1 2024 Budget', - 'description' => 'First quarter budget', - 'totalPrice' => '10000', - ]); - - // Send the request - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new PostBudgets($budget)); - - // Assert the request was sent - $mockClient->assertSent(PostBudgets::class); - - // Validate JSON:API structure - $mockClient->assertSent(function (Request $request) { - $body = $request->body()->all(); - - // Check for JSON:API 'data' wrapper - if (! isset($body['data'])) { - return false; - } - - $data = $body['data']; - - // Validate required JSON:API fields - if (! isset($data['type'])) { - return false; - } - - if (! isset($data['attributes'])) { - return false; - } - - // Validate that our budget data is in attributes - if ($data['attributes']['title'] !== 'Q1 2024 Budget') { - return false; - } - - if ($data['attributes']['description'] !== 'First quarter budget') { - return false; - } - - return true; - }); - - expect($response->status())->toBe(201); -}); - -it('model has toJsonApi method that returns correct structure', function () { - $budget = new Budget([ - 'title' => 'Test Budget', - 'totalPrice' => '5000', - ]); - - $jsonApi = $budget->toJsonApi(); - - expect($jsonApi)->toHaveKey('data'); - expect($jsonApi['data'])->toHaveKey('type'); - expect($jsonApi['data'])->toHaveKey('attributes'); - expect($jsonApi['data']['type'])->toBe('budgets'); // Pluralized camelCase - expect($jsonApi['data']['attributes'])->toBeArray(); - expect($jsonApi['data']['attributes'])->toHaveKey('title'); - expect($jsonApi['data']['attributes']['title'])->toBe('Test Budget'); -}); diff --git a/tests/MarkAsExportedTest.php b/tests/MarkAsExportedTest.php new file mode 100644 index 0000000..7cfdeb8 --- /dev/null +++ b/tests/MarkAsExportedTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the postOvertimeMarkAsExported method in the MarkAsExported resource', function () { + Saloon::fake([ + PostOvertimeMarkAsExportedRequest::class => MockResponse::fixture('markAsExported.postOvertimeMarkAsExported'), + ]); + + $response = $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported( + overtime: 'test string' + ); + + Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/MarkAsInvoicedTest.php b/tests/MarkAsInvoicedTest.php new file mode 100644 index 0000000..3bc6b8b --- /dev/null +++ b/tests/MarkAsInvoicedTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the postEntryMarkAsInvoiced method in the MarkAsInvoiced resource', function () { + Saloon::fake([ + PostEntryMarkAsInvoicedRequest::class => MockResponse::fixture('markAsInvoiced.postEntryMarkAsInvoiced'), + ]); + + $response = $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced( + entry: 'test string' + ); + + Saloon::assertSent(PostEntryMarkAsInvoicedRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/MeTest.php b/tests/MeTest.php new file mode 100644 index 0000000..9101b98 --- /dev/null +++ b/tests/MeTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getMes method in the Me resource', function () { + Saloon::fake([ + GetMesRequest::class => MockResponse::fixture('me.getMes'), + ]); + + $response = $this->timaticConnector->me()->getMes( + + ); + + Saloon::assertSent(GetMesRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/NumberTest.php b/tests/NumberTest.php new file mode 100644 index 0000000..84998fc --- /dev/null +++ b/tests/NumberTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getIncidentsNumber method in the Number resource', function () { + Saloon::fake([ + GetIncidentsNumberRequest::class => MockResponse::fixture('number.getIncidentsNumber'), + ]); + + $response = $this->timaticConnector->number()->getIncidentsNumber( + incident: 'test string' + ); + + Saloon::assertSent(GetIncidentsNumberRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/OvertimeTest.php b/tests/OvertimeTest.php new file mode 100644 index 0000000..bfd5f3d --- /dev/null +++ b/tests/OvertimeTest.php @@ -0,0 +1,50 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getOvertimes method in the Overtime resource', function () { + Saloon::fake([ + GetOvertimesRequest::class => MockResponse::fixture('overtime.getOvertimes'), + ]); + + $response = $this->timaticConnector->overtime()->getOvertimes( + filterstartedAt: 'test string', + filterstartedAteq: 'test string', + filterstartedAtnq: 'test string', + filterstartedAtgt: 'test string', + filterstartedAtlt: 'test string', + filterstartedAtgte: 'test string', + filterstartedAtlte: 'test string', + filterstartedAtcontains: 'test string', + filterendedAt: 'test string', + filterendedAteq: 'test string', + filterendedAtnq: 'test string', + filterendedAtgt: 'test string', + filterendedAtlt: 'test string', + filterendedAtgte: 'test string', + filterendedAtlte: 'test string', + filterendedAtcontains: 'test string', + filterisApproved: 'test string', + filterapprovedAt: 'test string', + filterapprovedAteq: 'test string', + filterapprovedAtnq: 'test string', + filterapprovedAtgt: 'test string', + filterapprovedAtlt: 'test string', + filterapprovedAtgte: 'test string', + filterapprovedAtlte: 'test string', + filterapprovedAtcontains: 'test string', + filterisExported: 'test string' + ); + + Saloon::assertSent(GetOvertimesRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/PaginationTest.php b/tests/PaginationTest.php deleted file mode 100644 index 62c3007..0000000 --- a/tests/PaginationTest.php +++ /dev/null @@ -1,112 +0,0 @@ - [ - ['type' => 'budgets', 'id' => '1', 'attributes' => ['title' => 'Budget 1']], - ['type' => 'budgets', 'id' => '2', 'attributes' => ['title' => 'Budget 2']], - ], - 'links' => [ - 'next' => 'https://api.app.timatic.test/budgets?page[number]=2&page[size]=2', - 'first' => 'https://api.app.timatic.test/budgets?page[number]=1&page[size]=2', - ], - ]), - // Page 2 - has next link - MockResponse::make([ - 'data' => [ - ['type' => 'budgets', 'id' => '3', 'attributes' => ['title' => 'Budget 3']], - ['type' => 'budgets', 'id' => '4', 'attributes' => ['title' => 'Budget 4']], - ], - 'links' => [ - 'next' => 'https://api.app.timatic.test/budgets?page[number]=3&page[size]=2', - 'prev' => 'https://api.app.timatic.test/budgets?page[number]=1&page[size]=2', - ], - ]), - // Page 3 - last page, no next link - MockResponse::make([ - 'data' => [ - ['type' => 'budgets', 'id' => '5', 'attributes' => ['title' => 'Budget 5']], - ], - 'links' => [ - 'next' => null, - 'prev' => 'https://api.app.timatic.test/budgets?page[number]=2&page[size]=2', - ], - ]), - ]); - - $timatic = $this->timatic->withMockClient($mockClient); - - // Create paginator - $paginator = $timatic->paginate(new GetBudgets); - $paginator->setPerPageLimit(2); - - // Collect all items - $allBudgets = []; - foreach ($paginator->items() as $budget) { - $allBudgets[] = $budget; - } - - // Should have collected 5 budgets total across 3 pages - expect($allBudgets)->toHaveCount(5); - - // Verify all 3 requests were made - expect($mockClient->getRecordedRequests())->toHaveCount(3); - - // Verify first request has correct pagination params - $firstRequest = $mockClient->getRecordedRequests()[0]; - expect($firstRequest->query()->get('page[number]'))->toBe(1); - expect($firstRequest->query()->get('page[size]'))->toBe(2); - - // Verify second request incremented page number - $secondRequest = $mockClient->getRecordedRequests()[1]; - expect($secondRequest->query()->get('page[number]'))->toBe(2); - expect($secondRequest->query()->get('page[size]'))->toBe(2); - - // Verify third request - $thirdRequest = $mockClient->getRecordedRequests()[2]; - expect($thirdRequest->query()->get('page[number]'))->toBe(3); -}); - -it('stops pagination when links.next is null', function () { - $mockClient = new MockClient([ - // Single page - no next link - MockResponse::make([ - 'data' => [ - ['type' => 'budgets', 'id' => '1', 'attributes' => ['title' => 'Budget 1']], - ], - 'links' => [ - 'next' => null, - 'first' => 'https://api.app.timatic.test/budgets?page[number]=1', - ], - ]), - ]); - - $timatic = $this->timatic->withMockClient($mockClient); - $paginator = $timatic->paginate(new GetBudgets); - - $allBudgets = []; - foreach ($paginator->items() as $budget) { - $allBudgets[] = $budget; - } - - // Should only have 1 budget - expect($allBudgets)->toHaveCount(1); - - // Should only have made 1 request - $mockClient->assertSentCount(1); -}); - -it('request implements Paginatable interface', function () { - $request = new GetBudgets; - - expect($request)->toBeInstanceOf(Paginatable::class); -}); diff --git a/tests/PeriodTest.php b/tests/PeriodTest.php new file mode 100644 index 0000000..528952c --- /dev/null +++ b/tests/PeriodTest.php @@ -0,0 +1,25 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getBudgetPeriods method in the Period resource', function () { + Saloon::fake([ + GetBudgetPeriodsRequest::class => MockResponse::fixture('period.getBudgetPeriods'), + ]); + + $response = $this->timaticConnector->period()->getBudgetPeriods( + budget: 'test string' + ); + + Saloon::assertSent(GetBudgetPeriodsRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/Pest.php b/tests/Pest.php index c2e8ade..79a7abd 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,20 +1,5 @@ 'https://api.app.timatic.test', - 'timatic.api_token' => null, - ]; - - return $config[$key] ?? $default; - } -} - -uses()->beforeEach(function () { - $this->timatic = new TimaticConnector; -})->in(__DIR__); +uses(TestCase::class)->in(__DIR__); diff --git a/tests/TeamTest.php b/tests/TeamTest.php new file mode 100644 index 0000000..1bd5b3f --- /dev/null +++ b/tests/TeamTest.php @@ -0,0 +1,85 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getTeams method in the Team resource', function () { + Saloon::fake([ + GetTeamsRequest::class => MockResponse::fixture('team.getTeams'), + ]); + + $response = $this->timaticConnector->team()->getTeams( + + ); + + Saloon::assertSent(GetTeamsRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the postTeams method in the Team resource', function () { + Saloon::fake([ + PostTeamsRequest::class => MockResponse::fixture('team.postTeams'), + ]); + + $response = $this->timaticConnector->team()->postTeams( + + ); + + Saloon::assertSent(PostTeamsRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getTeam method in the Team resource', function () { + Saloon::fake([ + GetTeamRequest::class => MockResponse::fixture('team.getTeam'), + ]); + + $response = $this->timaticConnector->team()->getTeam( + team: 'test string' + ); + + Saloon::assertSent(GetTeamRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the deleteTeam method in the Team resource', function () { + Saloon::fake([ + DeleteTeamRequest::class => MockResponse::fixture('team.deleteTeam'), + ]); + + $response = $this->timaticConnector->team()->deleteTeam( + team: 'test string' + ); + + Saloon::assertSent(DeleteTeamRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the patchTeam method in the Team resource', function () { + Saloon::fake([ + PatchTeamRequest::class => MockResponse::fixture('team.patchTeam'), + ]); + + $response = $this->timaticConnector->team()->patchTeam( + team: 'test string' + ); + + Saloon::assertSent(PatchTeamRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 0000000..dd8abe2 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,31 @@ +load(); + } + + // Set config values for testing + $app['config']->set('timatic.base_url', env('TIMATIC_BASE_URL', 'https://api.app.timatic.test')); + $app['config']->set('timatic.api_token', env('TIMATIC_API_TOKEN')); + } +} diff --git a/tests/TimaticResponseTest.php b/tests/TimaticResponseTest.php deleted file mode 100644 index c66f6e6..0000000 --- a/tests/TimaticResponseTest.php +++ /dev/null @@ -1,215 +0,0 @@ - [ - ['type' => 'budgets', 'id' => '1', 'attributes' => ['title' => 'Budget 1']], - ['type' => 'budgets', 'id' => '2', 'attributes' => ['title' => 'Budget 2']], - ], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response)->toBeInstanceOf(TimaticResponse::class); - - $firstItem = $response->firstItem(); - expect($firstItem)->toBeArray(); - expect($firstItem['id'])->toBe('1'); - expect($firstItem['attributes']['title'])->toBe('Budget 1'); -}); - -it('returns single item for non-collection response', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [ - 'type' => 'budgets', - 'id' => '1', - 'attributes' => ['title' => 'Budget 1'], - ], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - $firstItem = $response->firstItem(); - expect($firstItem)->toBeArray(); - expect($firstItem['id'])->toBe('1'); - expect($firstItem['attributes']['title'])->toBe('Budget 1'); -}); - -it('detects errors in response', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'errors' => [ - [ - 'status' => '404', - 'title' => 'Not Found', - 'detail' => 'Budget not found', - ], - ], - ], 404), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->hasErrors())->toBeTrue(); - expect($response->errors())->toBeArray(); - expect($response->errors())->toHaveCount(1); - expect($response->errors()[0]['status'])->toBe('404'); - expect($response->errors()[0]['title'])->toBe('Not Found'); -}); - -it('returns empty array when no errors exist', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [ - ['type' => 'budgets', 'id' => '1'], - ], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->hasErrors())->toBeFalse(); - expect($response->errors())->toBeArray(); - expect($response->errors())->toHaveCount(0); -}); - -it('returns meta information from response', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [], - 'meta' => [ - 'total' => 100, - 'page' => 1, - 'perPage' => 20, - ], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->meta())->toBeArray(); - expect($response->meta()['total'])->toBe(100); - expect($response->meta()['page'])->toBe(1); - expect($response->meta()['perPage'])->toBe(20); -}); - -it('returns empty array when no meta exists', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->meta())->toBeArray(); - expect($response->meta())->toHaveCount(0); -}); - -it('returns links from response', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [], - 'links' => [ - 'self' => 'https://api.app.timatic.test/budgets', - 'first' => 'https://api.app.timatic.test/budgets?page[number]=1', - 'next' => 'https://api.app.timatic.test/budgets?page[number]=2', - ], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->links())->toBeArray(); - expect($response->links()['self'])->toBe('https://api.app.timatic.test/budgets'); - expect($response->links()['next'])->toBe('https://api.app.timatic.test/budgets?page[number]=2'); -}); - -it('returns empty array when no links exist', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->links())->toBeArray(); - expect($response->links())->toHaveCount(0); -}); - -it('returns included resources from response', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [ - [ - 'type' => 'budgets', - 'id' => '1', - 'attributes' => ['title' => 'Budget 1'], - 'relationships' => [ - 'customer' => [ - 'data' => ['type' => 'customers', 'id' => '10'], - ], - ], - ], - ], - 'included' => [ - [ - 'type' => 'customers', - 'id' => '10', - 'attributes' => ['name' => 'Acme Corp'], - ], - ], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->included())->toBeArray(); - expect($response->included())->toHaveCount(1); - expect($response->included()[0]['type'])->toBe('customers'); - expect($response->included()[0]['id'])->toBe('10'); - expect($response->included()[0]['attributes']['name'])->toBe('Acme Corp'); -}); - -it('returns empty array when no included resources exist', function () { - $mockClient = new MockClient([ - MockResponse::make([ - 'data' => [], - ]), - ]); - - $response = $this->timatic - ->withMockClient($mockClient) - ->send(new GetBudgets); - - expect($response->included())->toBeArray(); - expect($response->included())->toHaveCount(0); -}); diff --git a/tests/TimeSpentTotalTest.php b/tests/TimeSpentTotalTest.php new file mode 100644 index 0000000..1726b0c --- /dev/null +++ b/tests/TimeSpentTotalTest.php @@ -0,0 +1,30 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getTimeSpentTotals method in the TimeSpentTotal resource', function () { + Saloon::fake([ + GetTimeSpentTotalsRequest::class => MockResponse::fixture('timeSpentTotal.getTimeSpentTotals'), + ]); + + $response = $this->timaticConnector->timeSpentTotal()->getTimeSpentTotals( + filterstartedAtgte: 'test string', + filterstartedAtlte: 'test string', + filterteamId: 'test string', + filterteamIdeq: 'test string', + filteruserId: 'test string', + filteruserIdeq: 'test string' + ); + + Saloon::assertSent(GetTimeSpentTotalsRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/UserCustomerHoursAggregateTest.php b/tests/UserCustomerHoursAggregateTest.php new file mode 100644 index 0000000..fcd15ba --- /dev/null +++ b/tests/UserCustomerHoursAggregateTest.php @@ -0,0 +1,56 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getUserCustomerHoursAggregates method in the UserCustomerHoursAggregate resource', function () { + Saloon::fake([ + GetUserCustomerHoursAggregatesRequest::class => MockResponse::fixture('userCustomerHoursAggregate.getUserCustomerHoursAggregates'), + ]); + + $response = $this->timaticConnector->userCustomerHoursAggregate()->getUserCustomerHoursAggregates( + filterstartedAt: 'test string', + filterstartedAteq: 'test string', + filterstartedAtnq: 'test string', + filterstartedAtgt: 'test string', + filterstartedAtlt: 'test string', + filterstartedAtgte: 'test string', + filterstartedAtlte: 'test string', + filterstartedAtcontains: 'test string', + filterendedAt: 'test string', + filterendedAteq: 'test string', + filterendedAtnq: 'test string', + filterendedAtgt: 'test string', + filterendedAtlt: 'test string', + filterendedAtgte: 'test string', + filterendedAtlte: 'test string', + filterendedAtcontains: 'test string', + filterteamId: 'test string', + filterteamIdeq: 'test string', + filterteamIdnq: 'test string', + filterteamIdgt: 'test string', + filterteamIdlt: 'test string', + filterteamIdgte: 'test string', + filterteamIdlte: 'test string', + filterteamIdcontains: 'test string', + filteruserId: 'test string', + filteruserIdeq: 'test string', + filteruserIdnq: 'test string', + filteruserIdgt: 'test string', + filteruserIdlt: 'test string', + filteruserIdgte: 'test string', + filteruserIdlte: 'test string', + filteruserIdcontains: 'test string' + ); + + Saloon::assertSent(GetUserCustomerHoursAggregatesRequest::class); + + expect($response->status())->toBe(200); +}); diff --git a/tests/UserTest.php b/tests/UserTest.php new file mode 100644 index 0000000..0469b5c --- /dev/null +++ b/tests/UserTest.php @@ -0,0 +1,86 @@ +timaticConnector = new Timatic\SDK\TimaticConnector; +}); + +it('calls the getUsers method in the User resource', function () { + Saloon::fake([ + GetUsersRequest::class => MockResponse::fixture('user.getUsers'), + ]); + + $response = $this->timaticConnector->user()->getUsers( + filterexternalId: 'test string', + filterexternalIdeq: 'test string' + ); + + Saloon::assertSent(GetUsersRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the postUsers method in the User resource', function () { + Saloon::fake([ + PostUsersRequest::class => MockResponse::fixture('user.postUsers'), + ]); + + $response = $this->timaticConnector->user()->postUsers( + + ); + + Saloon::assertSent(PostUsersRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the getUser method in the User resource', function () { + Saloon::fake([ + GetUserRequest::class => MockResponse::fixture('user.getUser'), + ]); + + $response = $this->timaticConnector->user()->getUser( + user: 'test string' + ); + + Saloon::assertSent(GetUserRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the deleteUser method in the User resource', function () { + Saloon::fake([ + DeleteUserRequest::class => MockResponse::fixture('user.deleteUser'), + ]); + + $response = $this->timaticConnector->user()->deleteUser( + user: 'test string' + ); + + Saloon::assertSent(DeleteUserRequest::class); + + expect($response->status())->toBe(200); +}); + +it('calls the patchUser method in the User resource', function () { + Saloon::fake([ + PatchUserRequest::class => MockResponse::fixture('user.patchUser'), + ]); + + $response = $this->timaticConnector->user()->patchUser( + user: 'test string' + ); + + Saloon::assertSent(PatchUserRequest::class); + + expect($response->status())->toBe(200); +}); From 86e3c405c95552530bc348191d73636b56f19f07 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 17 Nov 2025 23:22:52 +0100 Subject: [PATCH 18/62] add github workflow --- .github/workflows/code-style.yml | 31 ++++++++++++++++ .github/workflows/static-analysis.yml | 31 ++++++++++++++++ .github/workflows/tests.yml | 51 +++++++++++++++++++++++++++ README.md | 4 +++ 4 files changed, 117 insertions(+) create mode 100644 .github/workflows/code-style.yml create mode 100644 .github/workflows/static-analysis.yml create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/code-style.yml b/.github/workflows/code-style.yml new file mode 100644 index 0000000..a0f9324 --- /dev/null +++ b/.github/workflows/code-style.yml @@ -0,0 +1,31 @@ +name: Code Style + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +jobs: + pint: + runs-on: ubuntu-latest + + name: Laravel Pint + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: dom, curl, libxml, mbstring, zip + coverage: none + + - name: Install dependencies + run: composer install --prefer-dist --no-interaction + + - name: Run Laravel Pint + run: ./vendor/bin/pint --test diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml new file mode 100644 index 0000000..81fb6b9 --- /dev/null +++ b/.github/workflows/static-analysis.yml @@ -0,0 +1,31 @@ +name: Static Analysis + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +jobs: + larastan: + runs-on: ubuntu-latest + + name: Larastan + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.3 + extensions: dom, curl, libxml, mbstring, zip + coverage: none + + - name: Install dependencies + run: composer install --prefer-dist --no-interaction + + - name: Run Larastan + run: ./vendor/bin/phpstan analyse --memory-limit=2G diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..aaba036 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,51 @@ +name: Tests + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +jobs: + tests: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + php: [8.1, 8.2, 8.3, 8.4] + laravel: [10.*, 11.*, 12.*] + dependency-version: [prefer-lowest, prefer-stable] + exclude: + # Laravel 11+ requires PHP 8.2+ + - laravel: 11.* + php: 8.1 + - laravel: 12.* + php: 8.1 + + name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} (${{ matrix.dependency-version }}) + + 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 + 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 require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction + + - name: Run Pest tests + run: ./vendor/bin/pest diff --git a/README.md b/README.md index 17172c8..560a335 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Timatic PHP SDK +[![Tests](https://github.com/Timatic/timatic-php-sdk/actions/workflows/tests.yml/badge.svg)](https://github.com/Timatic/timatic-php-sdk/actions/workflows/tests.yml) +[![Code Style](https://github.com/Timatic/timatic-php-sdk/actions/workflows/code-style.yml/badge.svg)](https://github.com/Timatic/timatic-php-sdk/actions/workflows/code-style.yml) +[![Static Analysis](https://github.com/Timatic/timatic-php-sdk/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/Timatic/timatic-php-sdk/actions/workflows/static-analysis.yml) + A Laravel package for the Timatic API, built with [Saloon](https://docs.saloon.dev/) and automatically generated from OpenAPI specifications. ## Requirements From 460fb4d690a326e362e13ec854ec1629fa3146b4 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Tue, 18 Nov 2025 10:04:32 +0100 Subject: [PATCH 19/62] move generated tests to separate directory --- generator/JsonApiPestTestGenerator.php | 20 ++++++++++++++++++- generator/generate.php | 11 +--------- tests/{ => Requests}/ApproveTest.php | 2 +- tests/{ => Requests}/BudgetTest.php | 2 +- .../BudgetTimeSpentTotalTest.php | 2 +- tests/{ => Requests}/BudgetTypeTest.php | 2 +- tests/{ => Requests}/ChangeTest.php | 2 +- tests/{ => Requests}/CorrectionTest.php | 2 +- tests/{ => Requests}/CustomerTest.php | 2 +- tests/{ => Requests}/DailyProgressTest.php | 2 +- tests/{ => Requests}/EntriesExportTest.php | 2 +- tests/{ => Requests}/EntrySuggestionTest.php | 2 +- tests/{ => Requests}/EntryTest.php | 2 +- tests/{ => Requests}/EventTest.php | 2 +- tests/{ => Requests}/ExportMailTest.php | 2 +- tests/{ => Requests}/IncidentTest.php | 2 +- tests/{ => Requests}/MarkAsExportedTest.php | 2 +- tests/{ => Requests}/MarkAsInvoicedTest.php | 2 +- tests/{ => Requests}/MeTest.php | 2 +- tests/{ => Requests}/NumberTest.php | 2 +- tests/{ => Requests}/OvertimeTest.php | 2 +- tests/{ => Requests}/PeriodTest.php | 2 +- tests/{ => Requests}/TeamTest.php | 2 +- tests/{ => Requests}/TimeSpentTotalTest.php | 2 +- .../UserCustomerHoursAggregateTest.php | 2 +- tests/{ => Requests}/UserTest.php | 2 +- 26 files changed, 44 insertions(+), 35 deletions(-) rename tests/{ => Requests}/ApproveTest.php (95%) rename tests/{ => Requests}/BudgetTest.php (98%) rename tests/{ => Requests}/BudgetTimeSpentTotalTest.php (96%) rename tests/{ => Requests}/BudgetTypeTest.php (94%) rename tests/{ => Requests}/ChangeTest.php (96%) rename tests/{ => Requests}/CorrectionTest.php (97%) rename tests/{ => Requests}/CustomerTest.php (98%) rename tests/{ => Requests}/DailyProgressTest.php (95%) rename tests/{ => Requests}/EntriesExportTest.php (99%) rename tests/{ => Requests}/EntrySuggestionTest.php (98%) rename tests/{ => Requests}/EntryTest.php (99%) rename tests/{ => Requests}/EventTest.php (94%) rename tests/{ => Requests}/ExportMailTest.php (95%) rename tests/{ => Requests}/IncidentTest.php (96%) rename tests/{ => Requests}/MarkAsExportedTest.php (95%) rename tests/{ => Requests}/MarkAsInvoicedTest.php (95%) rename tests/{ => Requests}/MeTest.php (94%) rename tests/{ => Requests}/NumberTest.php (95%) rename tests/{ => Requests}/OvertimeTest.php (98%) rename tests/{ => Requests}/PeriodTest.php (95%) rename tests/{ => Requests}/TeamTest.php (98%) rename tests/{ => Requests}/TimeSpentTotalTest.php (96%) rename tests/{ => Requests}/UserCustomerHoursAggregateTest.php (98%) rename tests/{ => Requests}/UserTest.php (98%) diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 322f096..f4945c2 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -45,7 +45,7 @@ protected function getTestStubPath(): string /** * Use our custom JSON:API test function stub */ - protected function getTestFunctionStubPath(): string + protected function getTestFunctionStubPath(Endpoint $endpoint): string { return __DIR__.'/Stubs/pest-resource-test-func.stub'; } @@ -63,4 +63,22 @@ protected function getRequestClassName(Endpoint $endpoint): string return $className; } + + protected function getMethodName(Endpoint $endpoint, string $requestClassName): string + { + // Strip "Request" suffix if present to match actual Resource method names + $methodBaseName = str_ends_with($requestClassName, 'Request') + ? substr($requestClassName, 0, -7) + : $requestClassName; + + return NameHelper::safeVariableName($methodBaseName); + } + + /** + * Place tests in tests/Requests/ directory + */ + protected function getTestPath(string $resourceName): string + { + return "tests/Requests/{$resourceName}Test.php"; + } } diff --git a/generator/generate.php b/generator/generate.php index 1066685..96cc014 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -37,6 +37,7 @@ __DIR__.'/../src/Requests', __DIR__.'/../src/Resource', __DIR__.'/../src/Dto', + __DIR__.'/../tests/Requests', ]; foreach ($foldersToClean as $folder) { @@ -57,16 +58,6 @@ } } -// Clean up generated test files (but keep Pest.php and TestCase.php) -$testsFolder = __DIR__.'/../tests'; -if (is_dir($testsFolder)) { - $testFiles = glob($testsFolder.'/*Test.php'); - foreach ($testFiles as $testFile) { - unlink($testFile); - } - echo ' āœ“ Removed generated test files'."\n"; -} - echo "āœ… Cleanup completed\n\n"; // Parse the specification diff --git a/tests/ApproveTest.php b/tests/Requests/ApproveTest.php similarity index 95% rename from tests/ApproveTest.php rename to tests/Requests/ApproveTest.php index 59037cc..22b82d8 100644 --- a/tests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -1,6 +1,6 @@ Date: Thu, 20 Nov 2025 20:11:57 +0100 Subject: [PATCH 20/62] wip: dev setup + notes --- claude-todo.md | 23 +++++++++++++++++++++++ composer.json | 8 +++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 claude-todo.md diff --git a/claude-todo.md b/claude-todo.md new file mode 100644 index 0000000..eaf91cc --- /dev/null +++ b/claude-todo.md @@ -0,0 +1,23 @@ +Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. + +2. De classes moeten een {modelName}Id parameter hebbben. + +- Hiervoor moeten we de openapi spec file verbeteren. +- Vermoedelijk door scramble te gebruiken in Timatic API + +8. Documentation Updates + +- Update README.md met alle nieuwe features +- Voeg voorbeelden toe voor: +- Pagination usage +- Custom response methods +- Configuration options +- Update regenerate instructies met nieuwe patterns + + +11. Vereenvoudig de filters voor de collection requests + +- Verwijder de filters uit de constructor van de collection request classes +- Bekijk de implementatie van de filters in de intermax/api-client package +- Voeg een filter() method toe aan de collection request classes in de generator +- Update README.md met nieuwe filter instructies diff --git a/composer.json b/composer.json index f0859a6..485dd7b 100644 --- a/composer.json +++ b/composer.json @@ -27,8 +27,14 @@ "laravel/pint": "^1.0", "larastan/larastan": "^3.0", "orchestra/testbench": "^8.0|^9.0|^10.0", - "crescat-io/saloon-sdk-generator": "^1.4" + "crescat-io/saloon-sdk-generator": "dev-master" }, + "repositories": [ + { + "type": "path", + "url": "../saloon-sdk-generator" + } + ], "scripts": { "test": "pest", "format": "pint", From 5aae182de97fa5aceafda8d8d4e47ae5a284338f Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Thu, 20 Nov 2025 20:12:04 +0100 Subject: [PATCH 21/62] delete fixtures --- .../Saloon/approve.postOvertimeApprove.json | 20 ------------------- .../Fixtures/Saloon/budget.deleteBudget.json | 20 ------------------- tests/Fixtures/Saloon/budget.getBudget.json | 20 ------------------- tests/Fixtures/Saloon/budget.getBudgets.json | 20 ------------------- tests/Fixtures/Saloon/budget.patchBudget.json | 20 ------------------- tests/Fixtures/Saloon/budget.postBudgets.json | 20 ------------------- ...meSpentTotal.getBudgetTimeSpentTotals.json | 20 ------------------- .../Saloon/budgetType.getBudgetTypes.json | 20 ------------------- tests/Fixtures/Saloon/change.getChange.json | 20 ------------------- tests/Fixtures/Saloon/change.getChanges.json | 20 ------------------- .../Saloon/correction.patchCorrection.json | 20 ------------------- .../Saloon/correction.postCorrections.json | 20 ------------------- .../Saloon/customer.deleteCustomer.json | 20 ------------------- .../Fixtures/Saloon/customer.getCustomer.json | 20 ------------------- .../Saloon/customer.getCustomers.json | 20 ------------------- .../Saloon/customer.patchCustomer.json | 20 ------------------- .../Saloon/customer.postCustomers.json | 20 ------------------- .../dailyProgress.getDailyProgresses.json | 20 ------------------- .../entriesExport.getBudgetEntriesExport.json | 20 ------------------- tests/Fixtures/Saloon/entry.deleteEntry.json | 20 ------------------- tests/Fixtures/Saloon/entry.getEntries.json | 20 ------------------- tests/Fixtures/Saloon/entry.getEntry.json | 20 ------------------- tests/Fixtures/Saloon/entry.patchEntry.json | 20 ------------------- tests/Fixtures/Saloon/entry.postEntries.json | 20 ------------------- ...entrySuggestion.deleteEntrySuggestion.json | 20 ------------------- .../entrySuggestion.getEntrySuggestion.json | 20 ------------------- .../entrySuggestion.getEntrySuggestions.json | 20 ------------------- tests/Fixtures/Saloon/event.postEvents.json | 20 ------------------- .../exportMail.getBudgetsExportMails.json | 20 ------------------- .../Fixtures/Saloon/incident.getIncident.json | 20 ------------------- .../Saloon/incident.getIncidents.json | 20 ------------------- ...AsExported.postOvertimeMarkAsExported.json | 20 ------------------- ...arkAsInvoiced.postEntryMarkAsInvoiced.json | 20 ------------------- tests/Fixtures/Saloon/me.getMes.json | 20 ------------------- .../Saloon/number.getIncidentsNumber.json | 20 ------------------- .../Saloon/overtime.getOvertimes.json | 20 ------------------- .../Saloon/period.getBudgetPeriods.json | 20 ------------------- tests/Fixtures/Saloon/team.deleteTeam.json | 20 ------------------- tests/Fixtures/Saloon/team.getTeam.json | 20 ------------------- tests/Fixtures/Saloon/team.getTeams.json | 20 ------------------- tests/Fixtures/Saloon/team.patchTeam.json | 20 ------------------- tests/Fixtures/Saloon/team.postTeams.json | 20 ------------------- .../timeSpentTotal.getTimeSpentTotals.json | 20 ------------------- tests/Fixtures/Saloon/user.deleteUser.json | 20 ------------------- tests/Fixtures/Saloon/user.getUser.json | 20 ------------------- tests/Fixtures/Saloon/user.getUsers.json | 20 ------------------- tests/Fixtures/Saloon/user.patchUser.json | 20 ------------------- tests/Fixtures/Saloon/user.postUsers.json | 20 ------------------- ...regate.getUserCustomerHoursAggregates.json | 20 ------------------- 49 files changed, 980 deletions(-) delete mode 100644 tests/Fixtures/Saloon/approve.postOvertimeApprove.json delete mode 100644 tests/Fixtures/Saloon/budget.deleteBudget.json delete mode 100644 tests/Fixtures/Saloon/budget.getBudget.json delete mode 100644 tests/Fixtures/Saloon/budget.getBudgets.json delete mode 100644 tests/Fixtures/Saloon/budget.patchBudget.json delete mode 100644 tests/Fixtures/Saloon/budget.postBudgets.json delete mode 100644 tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json delete mode 100644 tests/Fixtures/Saloon/budgetType.getBudgetTypes.json delete mode 100644 tests/Fixtures/Saloon/change.getChange.json delete mode 100644 tests/Fixtures/Saloon/change.getChanges.json delete mode 100644 tests/Fixtures/Saloon/correction.patchCorrection.json delete mode 100644 tests/Fixtures/Saloon/correction.postCorrections.json delete mode 100644 tests/Fixtures/Saloon/customer.deleteCustomer.json delete mode 100644 tests/Fixtures/Saloon/customer.getCustomer.json delete mode 100644 tests/Fixtures/Saloon/customer.getCustomers.json delete mode 100644 tests/Fixtures/Saloon/customer.patchCustomer.json delete mode 100644 tests/Fixtures/Saloon/customer.postCustomers.json delete mode 100644 tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json delete mode 100644 tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json delete mode 100644 tests/Fixtures/Saloon/entry.deleteEntry.json delete mode 100644 tests/Fixtures/Saloon/entry.getEntries.json delete mode 100644 tests/Fixtures/Saloon/entry.getEntry.json delete mode 100644 tests/Fixtures/Saloon/entry.patchEntry.json delete mode 100644 tests/Fixtures/Saloon/entry.postEntries.json delete mode 100644 tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json delete mode 100644 tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json delete mode 100644 tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json delete mode 100644 tests/Fixtures/Saloon/event.postEvents.json delete mode 100644 tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json delete mode 100644 tests/Fixtures/Saloon/incident.getIncident.json delete mode 100644 tests/Fixtures/Saloon/incident.getIncidents.json delete mode 100644 tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json delete mode 100644 tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json delete mode 100644 tests/Fixtures/Saloon/me.getMes.json delete mode 100644 tests/Fixtures/Saloon/number.getIncidentsNumber.json delete mode 100644 tests/Fixtures/Saloon/overtime.getOvertimes.json delete mode 100644 tests/Fixtures/Saloon/period.getBudgetPeriods.json delete mode 100644 tests/Fixtures/Saloon/team.deleteTeam.json delete mode 100644 tests/Fixtures/Saloon/team.getTeam.json delete mode 100644 tests/Fixtures/Saloon/team.getTeams.json delete mode 100644 tests/Fixtures/Saloon/team.patchTeam.json delete mode 100644 tests/Fixtures/Saloon/team.postTeams.json delete mode 100644 tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json delete mode 100644 tests/Fixtures/Saloon/user.deleteUser.json delete mode 100644 tests/Fixtures/Saloon/user.getUser.json delete mode 100644 tests/Fixtures/Saloon/user.getUsers.json delete mode 100644 tests/Fixtures/Saloon/user.patchUser.json delete mode 100644 tests/Fixtures/Saloon/user.postUsers.json delete mode 100644 tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json diff --git a/tests/Fixtures/Saloon/approve.postOvertimeApprove.json b/tests/Fixtures/Saloon/approve.postOvertimeApprove.json deleted file mode 100644 index 77f88f0..0000000 --- a/tests/Fixtures/Saloon/approve.postOvertimeApprove.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Ijh2ZzVGRDRqU0tzbjZVNVZEd0YyMlE9PSIsInZhbHVlIjoiZSsyT1VFVzBGWlRhd3NqdUdVaGx6V0Via0hQeW1nbHNURVQvb2FoYWxQYThqM3FjcXUrVytJbjR1RklRcjZsTk5qdkpiQXYrYVhzZzZ2dmNWR3BZNG5QWXRjOHFTc2RKcmp3WkN1R3RRYkExdENCbHRaalQvNHcvRFhuR29BSEwiLCJtYWMiOiIwNjU3ZGU0YzZmMjg0ZmU0MGU2Yzc0NTg4ZjExNTY1YzJlNDQwZTZhYWU3MTA0ZTM0ODhkOTAzOTBhNWVhNmMyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Im04K3oyZmpNajE5bEt6UFVmcE1VM1E9PSIsInZhbHVlIjoieDJZZ1VGeSthSEdFZHZkOXM1SlBlT0JpSXhyM2U1MmFwNVg0LzZPNlY4SEFCc3JwRHdsZ0NyamsyV3hBNWVrQkVsU01teXZ5ckFHUmVwTFhOVmNFSEsxcGRZZFNRdVR1dUMzZnQ2Sk1wZmVRdWhyMlhMNXpHeHdramZsbmNTcisiLCJtYWMiOiJhNmQ2NTY2YWU0NzAzMzQzMzU5YTFhZjllMzIzNzc3ZThkZTUzMDAzM2EyMjVkYWRkMDRiNzAxNDQzMmUxODk0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.deleteBudget.json b/tests/Fixtures/Saloon/budget.deleteBudget.json deleted file mode 100644 index faf4a68..0000000 --- a/tests/Fixtures/Saloon/budget.deleteBudget.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImQvWkc5cVh0cjZvSDE5YWNQTGpPUmc9PSIsInZhbHVlIjoiR1BaYUkwRWZqSWZaK21mT0FJQnBUemRvVGdtaytZK3FORXdaL1RCelJYdTgvakk2MzRtTm9rdmFqSFMrZFVPOUlVRHIrWVR6MFQ3QzRGVnZnamloK3N5K3Mvd0JybC90UnVrY1hxc1pNRE1BRXNiYUoyMHRsR1U0YjRRTllBaGciLCJtYWMiOiI3YzQxZmJjYTkxOGQxYjJjNzUyM2Q4ZTNmYzRjZjQ0ZDc0NmZkM2I5ZDI0ZWY2YTkwNWRmYzk3MWFiNjY3NjE2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IklGQzdSQ1VsRGRuN3FIM3hudkJFMnc9PSIsInZhbHVlIjoicC9JOHdBTlRkMFBITlRCdWhITTgvY3JJNkVXRUFQcU5qNXNWNkVmcjd1Vm1tZHZxKzhNU280UVJ2UDF4bWNXSXZIeXVpVU5oQnFYd0FpY2NhUlE3dEVGVWdvRHRUUVZaZ3R2NG93dXRTWVJIdWxkenRxb21pWDVaeVJjVFI2ajgiLCJtYWMiOiI4NTFmN2UyZjM4MzQ4NGE4ODJkY2IxZDFlZDQwODk2ODlmMWFlMTU5MWI1MzA4ZDhmM2UxYWNiNWI4MzA1M2VkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.getBudget.json b/tests/Fixtures/Saloon/budget.getBudget.json deleted file mode 100644 index d740395..0000000 --- a/tests/Fixtures/Saloon/budget.getBudget.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Ijg1U1hoRkNCNUwweUlpZWFxaUtsMVE9PSIsInZhbHVlIjoiK1lIQWFCS1lPTmFIbzZXRnZEZ2ZqeWVJSWthVExXN3V0azRwRnZxV29GdTNTVkxHVC9BSGpjMHIrMW5kZFpUYXFDaWF0bkZEKzZ0VUQ1LzZhWkc5NzdyNXBWUzFQenpHYW5PSVF1R1ZsN3VLNTZQbStab3BmcXkvN0xuWGxlcUkiLCJtYWMiOiJkNmM4YWVjMTA1NTk4NWVjOGIyZjdmOGEyMWVjZDA3MzQxNGNkN2M0NDdhNDg3Y2EyMGNlNzhlMmQwYWIwYWE2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IjJGeVl1b1FHSk1KK0ZVZkhxSG5rTVE9PSIsInZhbHVlIjoiWVFXdExTMnpPQlRBRENsdXdCN0ZuL1dHODd5OGFIcjFaRU9Ob09VQkNqYTBMaGFuYTNHeTQ1dGtsRUVWYmRQV0FZS1pIOTRldGV3a0NWUEc0ckhiV1ZJVTBVUk1KSStVNVY2aHBZeElGdEdCbWt2QTIwMG1ubmg2SUx0N09FSWciLCJtYWMiOiJhNjJhM2I3ODMyM2U3NmU2NTMyMWQ1NWQxNmZiNzljMWEzZTNjZTgwYjE5ZjU3NDUwOWM0ODM5ZDVlNTkxZThlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.getBudgets.json b/tests/Fixtures/Saloon/budget.getBudgets.json deleted file mode 100644 index ecdfb7e..0000000 --- a/tests/Fixtures/Saloon/budget.getBudgets.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlRWbkVXVFlKRThLUVF3RERGeDBIUHc9PSIsInZhbHVlIjoiN0hpaG0razg3c3BOTDlUTE00YjQxWXg0N0lub08ySnZGSjF3YlRBLzZ3bWtvNVZBOTREVC9vSmVjOE9JZytZdUxiT3dVNmZROHd3d0F4NHA1UHJuSFVMZEJ0eGpKUUhQaXVSZXg1U2g3UEo4UVNxWEZCQVExSlFvRFI2WnlhbHMiLCJtYWMiOiIzZTA4YTczNjcyMDU0ZWM0MTM0ZjZjYmJkYzBhYjliMzk4NTNmYjRlMzc0YWNmZTg5NzIxYzc0ODg5NjFjMDg2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6ImNlUnZ5aDlvTzlZdkNSdHBnbjZ6cUE9PSIsInZhbHVlIjoiOUROSXpNUzVhZmxuazRNQ0R5ZmZHMFV2WjU3VHZiRVM5V0tDNUJJR081c0Z6WWVSaHVoNzV6VmdvWUlVS0hDSUNiZzFnYzAxWTFaOERKa285ZUx4SzA4RFUxa05JZzZNRFpMeGRyODBTa3Ruc0JlVEpiZXJmb3JhUCtrYVhFWFkiLCJtYWMiOiI0MGUxZDJmOGYwNGI4YjBhYmQzZmQ4N2E3MzIzM2M5Mzg3ODVjMDA0MDE3NzlmZjc4MTFkZmI2YjY4NWZhYzZlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.patchBudget.json b/tests/Fixtures/Saloon/budget.patchBudget.json deleted file mode 100644 index 2e1e0ee..0000000 --- a/tests/Fixtures/Saloon/budget.patchBudget.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImJaeGVDeVJhMWpkcFl6bGFDcHFCYkE9PSIsInZhbHVlIjoiNnhGV2lyZ0liZlhNT2grODJlY21PWGJkZEw2WjZ2ZDFJOFRMcUZqa3lROHlLMDc2TjZHbzlPRVkxZDkzZUJLQ3ZVVnJ5TW1ZSTd3bExVazlLa2hSWmFKVy9oekxJVXM3UW5HRGhjdnRwRTZjNHpwTU9EQSt1WlM2cTlSTVVwNC8iLCJtYWMiOiJhZmE2ZjMxOGUxZDcxOWFkMjAzZDMyOTE1MzQyN2YxZWE5ZGViMTExMzE2MmM1NzhjZDVhNDZjNjg1OTU2M2EyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Ik1RcVdLNm12VEpCVmF4R3d0VWx5MGc9PSIsInZhbHVlIjoicnhtRlljZThMaWozc0x3ZjZFL2h5eFR5T0d6T250QmwzZEpJZmdTOUlrSkE4Z2RMSlp3M3dHK3pCSDhhelc2ZmNjUFlSQ080Q04yQVJsTnVHN1hXc0lPK25XdGhGdlVKeTgrQmR4cmZXS1o5MzJ2R1ZPYUc2cHNYdGNKQW53b2kiLCJtYWMiOiIxZTYxMmJjMmEyMjk1YmVlYzhkYTM2ZWEyZTQ5NjU3MmZjYTNlNjY4NWY4Y2NmMWI3Zjc4OTIzN2IxMzgyNGViIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budget.postBudgets.json b/tests/Fixtures/Saloon/budget.postBudgets.json deleted file mode 100644 index 131433a..0000000 --- a/tests/Fixtures/Saloon/budget.postBudgets.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlBVb216cHhxaCtzSW1aMGtvSmNvV2c9PSIsInZhbHVlIjoiWXJCM1RaSzBocFJ2akt3ejI3TUEzV3RQYk1lY28ra1F1VW1rWVJpcHM1NnM2OUtZbHRsOEV2L3UwMTJUeHZKai9ORDZ2MGhDaVA1QUxlcXFGWXZBNmhEVUVqT1c3ZlQ3WlBEY2d0WTNZYXVEaUxBbjFGSWdEWW8xYTErL0JXUTAiLCJtYWMiOiI3ZWUxZjUwNDhiYTRhZDk2YWQ5OTBkNWE1MjM5MzhlM2Q3MGY4YTk5ZGI5NTQwOWE4NTQzNjAxMmEzZWM3Zjg0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InkwbzdralJ3RitDbXo0bmN4YWZjc1E9PSIsInZhbHVlIjoiS09KNitHTHYxckVnY2ViTVJxdHNXS01Lb25nZExhRnFEbDhXUHRzUnR1bnhXUHowYWZmaGQzbzNwUHJXemZCNmZrdkdXcVAzYnZSNHVvdTl1N1hpZDg5eFhOcU55aWd3aFBSc05oVlFtZ200ampHaFJjU0VxY296b3hHTk8ycG4iLCJtYWMiOiIwMzk2MGFiNDViYmIwNWM2MmYwODgzNWM4MmQ0Y2Q2MjMzODkwMWVjYmUzYzQ1YmMyMWNhNTEwOWFlYjlhNzFiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json b/tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json deleted file mode 100644 index 82daa5c..0000000 --- a/tests/Fixtures/Saloon/budgetTimeSpentTotal.getBudgetTimeSpentTotals.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IkRnaVlWRmM5aml3UUxsYk1RaGprdnc9PSIsInZhbHVlIjoiTUlWeXJna09DZFdReWtmNENxRjg1NEhhTkE2YnQ1WmlEcFBUUHJXQlRXWkRZaHhZRWorZ3JGTHduRnFobFlia3g2Vk9hSnlHZVB5MGNoNjN4STFueG9EN1VMMGs3Unc2dEp4RHh1bmlPNk8yNENYTjNuU3pBb2hIQ2JwMklHTlQiLCJtYWMiOiJiOTVmMGI2YmM3OWU3MGUyYzNhODY4YWIyYjQ4ZWZjOGQ3N2NlZjI2NTg0ZjQzNzZmMmQ5NjA4YTVjYzc5MGQ2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Im9wNml3Rm4zOWl6dlY4Ly83dmx1dXc9PSIsInZhbHVlIjoibWdoQmF5bWZCcytCZDhYZmpNWEpUdC93SGRITmpJd3drUUVUYXBWSjJJdWI0d1VRTm1KZlZvaVFKT2xzTVhwQTJoZDNnQldNOGhRbXJnbFU4UC9PTDF0d2F0Nk03UVJOZ0VQZklvTVFJRWJSNWU3RUhpSTZtTklXYUFHaFpaV2UiLCJtYWMiOiI4YjNjMjkxMmEzOTI1YmY2NjdhZWE1MDVlNjBmMTUxOWZlNzUxZmYyMmM4Y2JmOThlOWE0NGE5YmQzMDYxOTcwIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/budgetType.getBudgetTypes.json b/tests/Fixtures/Saloon/budgetType.getBudgetTypes.json deleted file mode 100644 index 0c1e31c..0000000 --- a/tests/Fixtures/Saloon/budgetType.getBudgetTypes.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IkRDN1Z2N2pRNUNMVW1Ec09RZ1c3WXc9PSIsInZhbHVlIjoiKys3bURqcUZFVytMUk5uUEtBdW5mNFJrTlhCUkN5MGtEMEt2UnJlTCs5RkdBUFQ3N1V5YUtBZWJUUjMrUkd3dngyRUZOQ0RYZmlyVG1nc0puc1J4SzFXeFJ3NjNNMy8ybkVuRFQvY09UZmJuRVl4c29Ga0piWFRQclFISzIzNXMiLCJtYWMiOiJlNDI2YjI5MzdkYjYzZWZjZGY4ZGY4YWY0MDM2YjAxOWE3N2I2ZmQ2Yjg0YzkzYmJkOGJhOTBjZmJhYmNlNGFkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IldnN1ZPTmp1SDVnSGRTdWFjLy9nalE9PSIsInZhbHVlIjoiT1d0eFhyZVV3RURMNVFJdGpFWklwREk0TmYyRlpiNFZYam9vMnFHNlhMZWNlek9IUFBUR2k4Q1lmQ2xTNnU3L1RydjRHMWMzWEFiSm5zcCs3WkFyYkkzeTlTU1RLbEc5UTJJWXFXdVI1cnZIS2JiWS9sZEtxeEFnK2ZGTUp3bzAiLCJtYWMiOiI0MzVhMDc5ODgxZDdjMWExOTUzMmY2ZThiZWU4NDdiNTdlOTBlNjc0MTM2NDY4ZTFmZjE5NzdhOGEwY2ExYmZhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/change.getChange.json b/tests/Fixtures/Saloon/change.getChange.json deleted file mode 100644 index 4edf0dc..0000000 --- a/tests/Fixtures/Saloon/change.getChange.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImU1SDBoYUpuSmRXVTdPcHdPSHhHWVE9PSIsInZhbHVlIjoicU5jMGs3Qk9reGQ2bDIxNnhwQnVXTWIwUkY4RTZJYVA5Wlh4OENjQlhNR2R6Zmphckx6UUdHeDhyMURFNjNtc0M5RTZPMkZxb1ZVRFc2dXMwSXpFVlNtVVFpY1Jqdnd3UUJTa0I5aU00SlIxMUpIY05lV0dXeTJaSFMvMlp5OXoiLCJtYWMiOiI5MDdiZGEyZjRjYjIyOTczZGM4M2ZhYTU3YjlkNGM3YTA3YTY4NDhjYTZlOWQ5OWJkZDRmMmFkZjkyYzdlMWYyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkMzTnl4ekhiTUJhaS9JbzlvcTAzTnc9PSIsInZhbHVlIjoiY2hwNFNzTFJ2QTAwS3B0RjlmUE1IWk9Wb3RVK0pZMU1jL0tXODJHRDV2aU5POHh6YjdiTy9PMlFrZE5qd3ZMSzJNdVhmdG1aT0hMT0pGZFZBTlMySCtLS3lFM3dKd3JNVW1lTGY4b2RKV01qQkJFREhxOEszRnN1UkUyNGY3eXgiLCJtYWMiOiIwMTI5YTJmMGI0NTgwN2RmYzVlYjg2YzJmNzFhODAzYmE4MDM5YTgxZTMzODIxYWQ4Nzk4ZWQyZmFlMzdiNGZjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/change.getChanges.json b/tests/Fixtures/Saloon/change.getChanges.json deleted file mode 100644 index e80281f..0000000 --- a/tests/Fixtures/Saloon/change.getChanges.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlZuSU5aU09FMjJMU0tZZ2hNUTZuYkE9PSIsInZhbHVlIjoiTERINlBJdWc3RVVCUGRMeE9xSU44U1Y2dkJnNnZseWpZMWdEb3lSdG5RQThTVHZiMU5LVWpUc1l2eVdWQ0RPbGt6MDg2aGxIS3lNSWJwZ2hJOVQwU3F1aUlnaGpNQlNwRFRRNnhkRVI2U0hCb0ViY0lZeTgra2hPLzBoRUd3L2MiLCJtYWMiOiIwNTc2ZmU1ZDhlOWU0NTEzY2YyODBiMmQzYjI0ZDJiMjc0OWNjOWM4Nzg4MzJkNjdjMGVlN2E0ZTBiZGY2NzQ5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6ImdVNEIvQldZYzJrL3E4eEo5aDlkTHc9PSIsInZhbHVlIjoiRUxmejBPN2ZRSkt2NlBWanlkTlo4VEJLYzhLRlhlKzYvOHBDdlVHeGFvSHBzWkVybUZERENnZUV4a0xyMEJkUVJ5R0FiWlBHdkVRa0U5ODdBQU1MUWU5STNEQ1JZUmRaZDZPRFpqZTc0ZUZTbHh3MTFIS0JHWjFCZDN3RXc3d2ciLCJtYWMiOiJjZGNlY2RjZjhlZjU1ZmMyOGQ3YWFlMjMzZGU3MmIwNjk4OWM4N2E2MDBmYzQ0MzZlNDJlYjZmNzE4MjY0ZDIzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/correction.patchCorrection.json b/tests/Fixtures/Saloon/correction.patchCorrection.json deleted file mode 100644 index 39d69d2..0000000 --- a/tests/Fixtures/Saloon/correction.patchCorrection.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImhhUjhMN3NMOGg3bFl4T0hBV0pZNkE9PSIsInZhbHVlIjoiNlRERkVNK1hHTHEwUXpTenhsWFlzdUdYYmJkRnVydlpCS2dPTFlqelBOTmNQekNhWEpjSmhhNlZnMlc1ODQ2emRldFVWK0VlRml2Mkw4d3Fkd2RSZnFuSGQ1eXJOUGNjaW13MGZLeXcwRlZ6WlczS3VXaWVWa1Z1bitvek9PY0QiLCJtYWMiOiIwNDI4MWMyZjRhNzJhYzliMTQwYTRlN2RmMzk2MWRhMzJmODgxZWMyOTQ1YzQ5Nzg2M2MwODk2MjZhOGEzYzBlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkgrbHFORG1rTzZ0R1RkMlZkL3o2ZWc9PSIsInZhbHVlIjoiQTJXeUU3Z0t5NExoOVhZTWtSekNkNldBSjUrOXU3TUFKN1Z0UzdTYVk2ajhnV1dhNWJ1cGlKR0xWVjVhUDJ5WDk4dlBCdHRBWkdVRXZwUTQrM0xqeEZ6Yk9ZaTFEeE56MWZHbk9HYWJBUGhXNjVqOUtHZ0ZFMER6ZlhJOGVZdVciLCJtYWMiOiJmZGU0ZjU0NGMxMTg3OTllYjY5ZmZkNDZiMzRlM2NjNDY5M2M5YWE1NjNmZjIwNDMwNDEyZDIwYzg5YWJmMWJkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/correction.postCorrections.json b/tests/Fixtures/Saloon/correction.postCorrections.json deleted file mode 100644 index 5ef98b1..0000000 --- a/tests/Fixtures/Saloon/correction.postCorrections.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IkVDNElCaEFDZ1lFS0d2RWowUmpRZnc9PSIsInZhbHVlIjoiVTBHZW5nYUVmYW53b0taL3BnNms2N29qMFN0SVhqWUhnT05oUmpRYWxJRHM4eVhicG1YWDZUMUQzKzd0Um9udTRGdEhnUHhIb01RVi9uS3NSQ1B0Z084MHVWc3RyMjI1TlQveEtqeWZWakdXV0FKY1pURzZDdzdUMTZWWS9wWk4iLCJtYWMiOiJmMzlhMmI0YTNjODZlMzVlMDMwMGY4Yzg5ZWE0YjlmNDQ5ZTU0Y2VmYjUwNjU1OTM2NTFjNzEyNWQ0MWY0ZTI5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlBqd3VmckhqTXl3U0xwUEdqbGVCbEE9PSIsInZhbHVlIjoiU21VTVgvdUlwcUhGa3NKVXB6eEFaRm40N01mazd3ay9IRTlaS3piTzcyWjhnb1EwY3Ewc0diUVNDTUJ2VkV0ZjFZOTFQcFRXRnNpczJORnRGOWxramphSFJ2eEpmRHZPaUdWcW1IendEWUR6dGtyRG5VT3ZzTEsvU2pwTHZneXAiLCJtYWMiOiJjOThmMmFjNDdkNzI1OTZjNWQ5NjhmODRkYTJlYzNkNzViMDg2NmRlNzU2ZmM5YjZkNWQwNDlmY2QzNTg3ZTFiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.deleteCustomer.json b/tests/Fixtures/Saloon/customer.deleteCustomer.json deleted file mode 100644 index 5eea56b..0000000 --- a/tests/Fixtures/Saloon/customer.deleteCustomer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6InhDZ3VIQWRDZkgwbE1oSGNENk5IZVE9PSIsInZhbHVlIjoiZDhLRFZBNlhhU01UbGlPdG5rVGErQitWOHA3dTh2YmtwdDhWVFRZUkQ3eGdIYVIxeUhYdXNYWmw0Mm5NMDgrRFVwVFlsUGpTbGVvd1hweWsrN1pEMVF2bmRZdW9wejQvdURhbW5SVjJnR2dST1BMNnVnUlAwNmRkQUZvYXZHZ2EiLCJtYWMiOiI1NGMwZDgwYWUwNWU1NzBhMTk5YjE0NzU5ZTBlMzcwZTRiMWU2N2VhYjljNTZkZjhiY2RkZDZhOGZhNGU2ZTQ1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InBEYmU3c2ZYTGRWVGVXTnlFQlZ3UkE9PSIsInZhbHVlIjoibXJxcTdrNzc1emhRTFZnQlRWbloxeVZVSVBwMUpudk03NVA2QVBwTTZVd1NrY3E5N2x6RGVkSllsUVNpbmhXS2F5MytwSzFWcHZkbXQzelFUZndpVE81THp0aFl6c3lTbE9UMVVGdEY2NXhZZWg2NG8zUWxMdWs5Y1dWMlNOWHEiLCJtYWMiOiJlNzZjNzIyMjBmYTY4YTkzNjU0MmIzOWVhODFlMTE1YTIzYTFlODE1ZjhmMmE2NGJlOWQ0NTlhM2JkNDlmNDhhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.getCustomer.json b/tests/Fixtures/Saloon/customer.getCustomer.json deleted file mode 100644 index 3f793c7..0000000 --- a/tests/Fixtures/Saloon/customer.getCustomer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImxzSWxYR0N6a21Rc3diSE5MazBvWFE9PSIsInZhbHVlIjoiSmxralp1ZVpTYnd3anUzaTBjVy9PajVsd0RMZERTZWlnSmxkNWhrM2tqRkx0b1dZYkY2WjJpcG9JZlJtTURHcGlKcTcrOTgvZFJZVnpGOWFmVjRpVEdlREwvdnV2U1d4S3dCUFFMYjJsSVZ1WlZyZjhocVhKOTZIRXM5TXVBRTUiLCJtYWMiOiIxYWQyMGM5MTZjNjEyNWE5NTNmOWE2MzJjY2RlOWYzMDBlMTcwZTdkN2EwMmU2YzA1MzVlYjFlN2FiOTA0OTk5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6ImJuYy9Sa0VtOUprUk1JaFo1alBudUE9PSIsInZhbHVlIjoiamlueUhlci95bnZvOTVJVU42SGNUQmtDemk1YUJvSEtuWDZ5NUNidFVhVkxSQ1M2TTd2NEVyUDBFanBaYTZpeTlKL3BBV3B4aTVLVVBRTGJ3WGNsUVpMaGZDQ3h5ckJKenIrekw3aEZFTVFhT3ZnTzRSdkxLUzlXdnNVSUpGOSsiLCJtYWMiOiI1YmZiN2ZiNjliMGQ4NTk5OWY3ZDNiYWI3ZTkxNmJjY2ZmOTdhYmJiNTMxYjc3M2M4ODBjMGZhMzYwYzQ3M2M3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.getCustomers.json b/tests/Fixtures/Saloon/customer.getCustomers.json deleted file mode 100644 index eeaa63b..0000000 --- a/tests/Fixtures/Saloon/customer.getCustomers.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IkFSeGoyWGYzd2xSb0R1MGlOcEh3Y3c9PSIsInZhbHVlIjoiaUFtWlBNemF3U21xaFFLT01FR3gyM0Q5MGJySGZJY3Rqa2ZtUzcxTVJpUit2Ny9XcytqV1YyOE8yZys4WnV5aXR5UDZtR1U5M2tZNnh5cUVrQnZDTnV0TVRKYXhnUkw3UE1SNmJsU0JWZnBuSEVtNE10NWROMDJMb0lGekJwLysiLCJtYWMiOiJkNDBjZTk0MjAzM2M0MmY4ZDQ4OTJiMDY0YmRhNmVjNWI0NmNmYjU1Y2E0MDI0MTE2M2M5NjM5NGNmMjZjZGNjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlhGZndDcnZmYXFRZWFjQnpkMFc0TFE9PSIsInZhbHVlIjoidmIvKzBrbzFCS01uTlZISUd1SDM2V0hwQlB0dUppZCsrTzEwaElKRWJGeTdpK21Fa1BGU3grZC9zQ2J2M1FQdFI3V1dtV0Rob20zaFNzREFrUnAwQ3NONHpOMkUyaG1scGc5dk5vSVhOUFNlV1ZBNkxnT0UvbEZsbUtydlVUZkMiLCJtYWMiOiJhOTBkYmIyYmYxNDJlZTlhOTQ1M2FhMzJhMDJmMGQzMjFmYjZmYjI3YWFlN2U2OGVmNGFkY2RiYmU3OTYxNmJkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.patchCustomer.json b/tests/Fixtures/Saloon/customer.patchCustomer.json deleted file mode 100644 index 43b2c76..0000000 --- a/tests/Fixtures/Saloon/customer.patchCustomer.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Ii9JMGR2NlF4QzYxSzhpQ2h2dFFrYWc9PSIsInZhbHVlIjoiN1VobEJCdDNLUzhXOEd6WFhhYjZzVWhSNFlWSGN3eGhoTVVGY3JxWVl2TzRmTEhUVHk1aWdDMUdFNUlTdXBSWmplc1ZhN2F5UkN0cFFoeVUrMDJiaFJnV0d4SndWZGRVL01XZ1B5N0c5N29yWGtWYUZJbCtuUEIyK0dvVDFhNmYiLCJtYWMiOiJmZGU2ODIyZThiZDRmOWQwODZkYjlmZjE4YjlhYjVkYzdjZmJjNDkxNDIyMzQ1ODdhMWZlNDQ2ZWY5MTQzZGYzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IllRajFHaWdJdGJvclNINXVqbDBGRWc9PSIsInZhbHVlIjoiVDYzTTNQaUdIamJ5M3J0MjZqUzY5ZkVlYy9BQXZKYVc3dGtQNk94TURobVU1eHprZGZ3UXBrRVNtV2dIcGx5Y2Rac2gzaTBvZ0hRY0t6Q29YK1Z3UDNqcGltQXUzRS9WOUk1ejJuSmtPMlZrUVVGaHFxL21SenhlWU5COFBmbGMiLCJtYWMiOiI3NDgzODkwNmY1MWY1Mzg4NTAxYTcyNmNkNWI0ZDExMjE5ZDQ5MjgyYmIzYTYzMmJmOWJmNzczNWMyYmRiYjM0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/customer.postCustomers.json b/tests/Fixtures/Saloon/customer.postCustomers.json deleted file mode 100644 index 6c3b8c4..0000000 --- a/tests/Fixtures/Saloon/customer.postCustomers.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImJTaG40cCs0ekFZbEgrclVJUXhramc9PSIsInZhbHVlIjoiMXEzanBOeFQ1SE83NlRIcTlQMDBhWVNCTDlNSTBaeTlQdzlFUWZpRE9CdXpuUVBpOEcrQjJaNTV6ZFhiWTBDM29qeTBNMUhISU9ySW1HdjJXR3V4b3pGZC9jSFdXamlnUHdXS3hZZFFPcWhNanUva3pMUDd0U2pLZFBEWUo3U0UiLCJtYWMiOiJhMjExMzZjODc3ZTMwNzRjYzJmZGYwZGZkMzVmMGZjYTAzODdiM2I0Mzc1YzdiMjcwY2VkMTBiZDRhYzExMjQ0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InlxYjFwbjJyQjc0L3BSTjZ5djg5Vnc9PSIsInZhbHVlIjoicmNNbEp3RjluWHk3dll1cUFjUGVYaHpmaGFjczU4RFNMT2RwT0hPcURyVU5VZ3pIWlY2VUFLK0l5MmlQOUVnTmdpeThFMy8zbHRZaUZsRC8rWktYQTdQMjMyNVFtTHJIeW1EcGlvM0pjd2ZKMS9xZEVaTDNoc1pTUC8rMS9kNkwiLCJtYWMiOiI2OWMxYzA5MDNjZjA0M2YyNjIzZDVlZjMwMmZjMDk3NTM2OGNlNzM0ZDUwYzZkY2E0NmU1NDUzM2RhN2UyNzAyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json b/tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json deleted file mode 100644 index 3a183f7..0000000 --- a/tests/Fixtures/Saloon/dailyProgress.getDailyProgresses.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImpRRVJ2ZjIwRXNvOWVuSERZWG5hMWc9PSIsInZhbHVlIjoiM3ZPVGtLYmJaVjFkenBZdUZjWTBIRkRLdFRDQ1ZsbGdtWExyZVZ4emtJUisyZWxHNW1PdjJaR0h0Q2k2NGNUbHQxTjYxbVcrSnRlbi9UVDNENTZsUzFJejloU2w4b3pSR1hoN0IrMzBXUHpLeEJqQW9rUlJCM2NWY2h3cFY2akgiLCJtYWMiOiJhNTgyNjc4YjNkM2FjYTdjY2QzODUxZTk2YThmYmUyM2YyNmQwYzdjOGY4OGI4YTQyOTNkOTFiN2FmMGQ4Mjc3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkpFdVpQWi95Si8rUlZTTkRmaDRwTlE9PSIsInZhbHVlIjoiZ2hBNUdOamNQYWpkaURxMWRlWkYxd3ZHTHI1dmpPc2ZCdTJxRVpOV0RqVXBwQXYxWExoL1V0WTBWUUtWMUxLdjBEWGVRV0VYdnFGdUdXbzM0aW8xUlV4clZyanZrUHd4Si80a1NiT3dvaU1mbXZYS2ptenYwZkxXb2RTRXNuMWQiLCJtYWMiOiI1MzdkZTk4OTdjNjI3ZmJhMDE3ZTlkMTM2MzFhNjU3MzhhZmJiYzI4OWE5YjA5Mzg3MGIyNDhiMmE3MWZjN2UzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json b/tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json deleted file mode 100644 index fbf16c7..0000000 --- a/tests/Fixtures/Saloon/entriesExport.getBudgetEntriesExport.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImdpcC91TzJkUWtRZ1JvS2hibUJQUWc9PSIsInZhbHVlIjoiYnBlSjVua2VzbTQybzFoSWxFS1Q0bllvNGtCR3VBYzlLVUxoekZ2cGlyTHNUNG9oZVhaVEFwYU9NOGY4UlZzZTBJaUxCU1A0OExHbnNTejNYTXhOQy9nK21KQWJBRjdKZ1A4NGh6V2t5bWlTaUxrTU1RSkozVlZDTGRBRFY3cXEiLCJtYWMiOiJmZDliYTFjYWYwY2Y0MGE3MzIxMWE4ZDVjZDgzMjdlMDIwYWQ2N2MwNjkwNjFkZDYxMWIzNjMwMzA1MDkxNzk1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IjVZQ245T3hReTBhWTlGUmJ3bmV6WkE9PSIsInZhbHVlIjoiS0txMXBhalBmY0tiYWRYMTU5Z25RTTBvZU9uMTNjeUFPK01RQzQ5TGFhMG5HYkY0WFFtL2J3aENZWWYyU1I1N0crZm5tUnA5dGg2Y20yMENndmxaaFpPL0EwOWlpY3BJZHBwNVdVTEh5MTd2cFhCYzh0UldXS1p4SzhYRUorVWoiLCJtYWMiOiJjZDkwNDZjNTU2YThhNDUyZjM3N2NhY2U4YmUwY2M5YzdhNmM2MzhkOWYyMTJmOTMzMTY1Yzk3NGFmMDQ0NDhiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.deleteEntry.json b/tests/Fixtures/Saloon/entry.deleteEntry.json deleted file mode 100644 index 25033e1..0000000 --- a/tests/Fixtures/Saloon/entry.deleteEntry.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Ijh5NnArcGxlejk3TC93NnVET1lValE9PSIsInZhbHVlIjoibnBscmZKWTlaU3hGODU1VTArMGhHeUxZcGlBSDBwZmZBaHBCR1lUOWsxNG5nM3FRWU50eU5VNHJObFd3OVNyUGdjNk5CTEIxaGM2enFKM1QxcFlmbFoyaVJ1UjBKM29Cd0UycHM3VXU2cStFb1hvdzdIVEV4L1RYSWVCRk5JSEwiLCJtYWMiOiI4MDMyM2I5N2Y0NjZiNjZmNzFhN2JiODQxYjE5NWI5NGQ4YWFkM2E2NTg1NDdhMGFiOWE5NWY3MTdiMWQ0MGNlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlZvNEVFSXFiUHNENVZaL2NWd1lib3c9PSIsInZhbHVlIjoib1l0NU5jcGNSeXh0cGgwZ3ZSS0lzVHhuLzlXMGpIU0NxNUltdGowR1J3cFZtN1V4SkI4eEhGeTJwRnl2WDMrNE85b2xGbTBDUlh3RnJZcjlveVE1VE9hUUl5U1NwTFN6TGFsTzRrbldoSXhoZ3p3NXo5YnU4RkpGclcvK1RFYmsiLCJtYWMiOiI4NTEyNTNmM2Q2ZmFkYWM4MjBjZDc1Y2M4ZjY3OGU1ZjFiNDExMjczMjk5MTAxZTVmYmUyZWYzMTBmMjA5ZGM0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.getEntries.json b/tests/Fixtures/Saloon/entry.getEntries.json deleted file mode 100644 index e14c569..0000000 --- a/tests/Fixtures/Saloon/entry.getEntries.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjVFdVMxOWF6dkhzMG5YL3QrSTloS0E9PSIsInZhbHVlIjoiZW5HZ0trOXk1TDUyNm5QeXBLSEx5Uzc2Sjh0VTBMYzFvelV3WjMrYWw4TGJqYi8vVHV6eTFqYm1JRWVMZ05Tdm1zUFhGUTd1ME5PdEthejRUaWJlNGlJMEQ5V2xRMFdCVmdvRDJHVGNMdktMQzYzbzQ4WHAxdUtvR3NETzVCeEIiLCJtYWMiOiJkYmFiN2VlMTNiOGE0ZDNkZTZjN2FjOTgzZjk4MWY1MjBiNDUwZmY0NGFkZDY2ODEzMThmZTI2YzgxYWYyMTQ3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IklwSmtyRzlzUlhGNyt0OS9HSDcyM3c9PSIsInZhbHVlIjoia09rZTM3N25LeDNDNU5UdTNIZkRGb3RtTEtUaGJDeWprb3E1aTE2SVptZDV2NWo4VE5pc0VHMndnQ2phM0xJdjUrWStIejlLbG9TNWpPSVY4VUw1UFFKQ3hzUlczeFZYYkd5Wm1lYzlLcWZXNUl2aHdTdStOZVhDdVQ4YVVKY1UiLCJtYWMiOiI4OWY1ODU1Yzc0ZDkxNmM0NDk0OTQyNzIxZmQ4MmRiNTJiOWUzNmU4YmU4Y2NhMjQyZjgxMmFhMjg4YjM4ZDkxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.getEntry.json b/tests/Fixtures/Saloon/entry.getEntry.json deleted file mode 100644 index 604c8f8..0000000 --- a/tests/Fixtures/Saloon/entry.getEntry.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IllDSXMxRnZjZlRkMEZPUk5nNjlHVnc9PSIsInZhbHVlIjoiMmcvckNkMjVIYW54ZjBRUHlVbmJHdDlXNUxZbWdsbmpDNFpYT1hPaVdDMHVEcTR0YzM2Wm5ZS2JseUJsY1BUUU1nN1RMM0RJM0FCQlV4bVNTMlQySEMwdENYRmVRNHU4SHlpekcyNFRHRlYxY2lRUEVLTlVSZVpJV1NWSExLZ2YiLCJtYWMiOiI3NDU4MGE1MWFmNzNiMzVlODYzYjBjNDg3MTU0YTgxM2EyMjI4ZWVjNzI1ZDEzMzFjOTI3YWJjMTEwZGE5MjI2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IklveGQyK014U2FKWm41L0hxZHBiblE9PSIsInZhbHVlIjoiWis1WlB2WUFiR3VkZmZqcTZQRVA0K3Vmc2ZjSHIwTFlxd0tRRXZSVGZTT0lqY3IzTXpRRzhYU2haUWswbzJ5aWM4SzNJOHF1Vk9kK0kyVFNjK3UyZUY3WHk2ZDYzWmJkai9yN3piQkt6VTdrcjNnYlYvWkovbzgwR2JISVg5OG4iLCJtYWMiOiI4OTM5MzBhMmZhOTFmNDRmZWY0NjkxNzNhM2Q0NzljMzM2YjQ4NTNkZjlhZTZmZjdhMmQxMzZlOTY5MDBiNmVhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.patchEntry.json b/tests/Fixtures/Saloon/entry.patchEntry.json deleted file mode 100644 index 67f0882..0000000 --- a/tests/Fixtures/Saloon/entry.patchEntry.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlVkZEhDeFp1c0dQR1BmMXZLTnh0alE9PSIsInZhbHVlIjoiSlhCeGdUaHlzWDQ2ZjBUZ3FJWE5QSDlFNkFBdmxqWWcxRlQrNXhpbzF0aVRUaFFUMDJBV01BajQyWGV0TjlQOUxmaEc0dVpyWW1xZnY5U0JYeE85MEVyeUJ1S1hhZ25yRXd5YlJQR0UwUHZnNklqSkQ1ZU5ubU13S0NmcHM2MEsiLCJtYWMiOiI0MzRkZWNjMGMyMTc5YTQyZmRlNGZlZjhhNWU4MzMxY2UyNWZjZjJkMmJiMDE0MzBhYjk2ZDM3NmM5YjFiMjdjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IjNHRW8wWkwwTGMwUzBDWEpmTU5BM3c9PSIsInZhbHVlIjoiRURnZXBrMThJMm9ONFIxNGp5NlgvT0FFc3lobVdNaDZicTNKdnZZb2NTcFFCUE9DQlBiZzZQY1JZT3dpZ0hQWS90OU0rc3dIU3VwbVNSMUNiWFQ2OU05Y3lTcjBadVpXK3pqcFFNNGI5M3NoK0pjOXgrQlhxYXFhU1AweEtMbGwiLCJtYWMiOiI5ODQ4YzcxZTI5M2E1NWY2ZjAxNjU3M2Q5Y2FjNzAzYzdkOWM3NjExZjZkNWZhNThkN2FlNzY0NjlmZjhhNDUzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entry.postEntries.json b/tests/Fixtures/Saloon/entry.postEntries.json deleted file mode 100644 index b703869..0000000 --- a/tests/Fixtures/Saloon/entry.postEntries.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjVKTDdLaFBKUmRDZ09PNVkyVzN1aGc9PSIsInZhbHVlIjoiU3hSTEljQ1hHMEZXOFdqZUlXb25xNHVCVFNDcGI2c2twait5ZHl1K0N4RjNONTZJNXJoYVNkcDRNRGxudTdBOUxrUFZvYXJCUG45VGdLK0MwamQ5WDlWL1FPNEZqVVYwV2Q4QzRvMEU5NjMzZU1XZ3FXRDEvNytnazVSMnNMY3oiLCJtYWMiOiI2NWY5ZGM3NGZhZDBkYTA1ZjBkZmNlODAyYTM5ZGFlZDI5ZDNmMjc2MmQ3NGQzYjRkYTlmZTIxN2JjODVlZmNiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6ImVNVnViMnJYY1pySnlmY05UOFVYYVE9PSIsInZhbHVlIjoiWWQ4U1hFcjlBOUxmaXFyNzY2cVVKVGZpbmlTMTNBQ0g0TzlIU1hsNkxnRlRlTmJEbEhVY1R4ZDhGVVYyUmltSTdvVm9teHdnZUNHN3NockZ3Rk03NXJ1RUZtUENub0hYTmpCUzBHL1ZTemdXU0V4a3JabG8yRy9sNFFMakJTWEEiLCJtYWMiOiJkZjcyNWVlMmY4MTQyOWRkNGE3Zjc4NjIwM2QzYmMzMzRhODRlYzBjNWUwYTZlMmQwZDdmNDJkMDA1YTIzMWQwIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json b/tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json deleted file mode 100644 index e0fcd17..0000000 --- a/tests/Fixtures/Saloon/entrySuggestion.deleteEntrySuggestion.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImpsN01WTlVrNFFmY2c2cjVxRTJOb3c9PSIsInZhbHVlIjoiMkZkUVkxMHVUa1ZNTzFJK0wxTFc2M1VHeGJpcm9JejQ0Slplb252YlB4MlN1TWxHbEYyS2tnb01rRmFpVk1tekRQWEc2bGxXS0tOQ09wbllkakIxNUxEM0RpRFR4SnQvb0FxUzI5WjJVMnAva3lkZER3c2hObGVLWU9UcVZHdSsiLCJtYWMiOiIxMzQ0NTBkNTUzMTllMWYwMzU0MzljYzY4YTgyMWEzMGE2ODQyNGRmMmYyODQ0MmVhYTllMWIzNWExZTJiMTdjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkpWVk11cW1IbjdHSXV3S3NCc3Y2eFE9PSIsInZhbHVlIjoiTnJ0Nmxva3FVVUlTSzFiL1RLM2ZnbkZxUWUyM0JhOWZ5d2h4TVFESXRTdjVNaURPbGJGL1JlYVZtSWMvTTdsdThSbEprZ3pJS3dWRlg2aXdCRWdNNXE4aFh0SUhmWUhMNUhwL0pvNE9KdkRPWjFtNi9jNVZzY0hqcWlCY2JZajQiLCJtYWMiOiIyOWY3ZjVkNzk5NWVmNzcwMGRlMjJlZTY0MmQwY2JjNWIwYzkxZDgyZWFlZWM5NGZmNjM5NGI1MjgyZDFhZGY1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json b/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json deleted file mode 100644 index dbfe902..0000000 --- a/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestion.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjhSL2V6cjFZMU8xQit0ckgxeENReVE9PSIsInZhbHVlIjoiRk1Jc2E3OWJjMzl4dENDWStIbEcvWWdQQ0lPSXV0S1UyN3BQdUhLUUQwVVk5SERPNXljRkt2SnlHQ0lrZWI4bkNvVEVjdERmZ0tjNWdYVm82MHNtYU9Tck50YW53U2xPTG1tM1B4Nk5LVE0vaUZKdXBxcjdEa2dCSnpuVUx6eFMiLCJtYWMiOiI2MmQzOTRiNDJhODJiYzJlODNkMzAxN2E5MzQ3MWUxODdhMzNmZmFmZTczY2U5ZGZkZDU1MDJkODgyODNmMzgxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkdzSDA0NnErcDE5YUtDTkNDaGNTd1E9PSIsInZhbHVlIjoiQ1lDZGNxaFNjK0JOVkRjVzdpN3FubndqWWJacHpZNGUvelpmSE9HVHhhcGNNTVFkOXlwczRpM3BSdDJxbC9VZ0VvOWhndDNXMTl3THdkbzdYdkh4c3dLZ005UU8zejFlcmRWTU5pcDdWR0VzS3V4V0dxYW1MQ0JMRTZvQVAyNzciLCJtYWMiOiJhZDFkYWRlNWY4YmExYWFlOGE5MzNhOTcyYTQ5Y2Y3ZjlhZDJlMzc2OWVkMTliZmY0MGUzMTdhYWJkNzM5NGY3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json b/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json deleted file mode 100644 index 5e27f2e..0000000 --- a/tests/Fixtures/Saloon/entrySuggestion.getEntrySuggestions.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IldvU0pONEhyblV6VE5mQ1hXd25kYUE9PSIsInZhbHVlIjoiV2ZtczJ4TDhETW45Y1gvTEVoR0RSeVFGQ1pxTHRFd2kzMUR3aW9JZW10OXlLNDdIVDdnQW9Jcm03dkRMVmU3ZCtFaW9jRFhraCtOcTlIQ2lnMDNiUmMvbi9qZUp3MytacFRIdWNiUWtsakU1MC82WGk0MFRSV1p6amNlQzlDS3kiLCJtYWMiOiIwMjk1NjBmNmE4MTdlM2NlOWY3MzJkYjVkMWQxNTcyNDg5Njk4Zjg2Nzg1YjRhMWZiMDM4MzI4NmY3MmExNmZkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Im9iT3hrMU1kZEJtVmRLOGpSbm9lNWc9PSIsInZhbHVlIjoiaGNpeDF6bUpkdDNFRE5yTFBvdUtnK0ZSVi9tQ0wwdm81NCt3M1Jyc3dEWCtyQXdvV2pCd0lCNGduMTYrVVZDVHF2aE1nWko4R1ZSc0owZmlWeGpMMExaeUQ5TEVQdjlkNGZEVHNwYkVoclhwVmlFRmJub2NKVCtPYXZUTCs3RmYiLCJtYWMiOiI4NTcwMDI1Y2M0ZmIyZWM5YTU4M2ZlZDI0YzAzYzJjM2FlYzZiNTY0NWFkMGY5NmM1N2Q3OWQ2OGY2NmRmNjEzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/event.postEvents.json b/tests/Fixtures/Saloon/event.postEvents.json deleted file mode 100644 index 3a3fb96..0000000 --- a/tests/Fixtures/Saloon/event.postEvents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjRzNWtVd0VYQkphaDVHOUorZ3JxcGc9PSIsInZhbHVlIjoiUFg1ajZpZDZwR2kyNG16dGJlUE0zVlFFWDFOaWp5NHZ0SGRZT2p6UnhuYy9YQkhtV3Z0RlJaUUdmbTA4RnZQby9Na0VFckpiZlkwMnBObWw2SzVhcmRQOG9MSUorSkMwY2FHa1VMZVd0am14azc0aHhaNTd3T3lRbE5XdFhSQkQiLCJtYWMiOiJhYTUyNjdhMTZhMjg2MTBlNGVjMjYwYWE4YWU1NDRkMjk0Njk5N2Y4M2RjZGQxZjk5MmY4NGIxMWJkYzQ0YWIyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkkvaGExemQzd3VSU3pKSUpuaE1TSFE9PSIsInZhbHVlIjoiL3dqQVFqRVZDR0JiRlpZeXBDNHVSWGQ5bTI2bjZ5a25aVTFxYkI4UHlVZzBTSkFLN3RuWDg3VGpsMHV0ZWhhdFRHYnlxR1FpVUZBa2tmRk9QTkFCM0R0c3M1UGpoUDBDamRwbTBGUmRFY2VFelRXQXFYcnp1VEEyRVdiMUM3ZjUiLCJtYWMiOiJiNWNjMmNjNjVhZDM4Njc0OTg5M2E3NGI4ZTI1ODg0MTlmNTQ1YjE5YzZkMGU0ZmZhMzgwNDkyYzQyMTI1NTlmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json b/tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json deleted file mode 100644 index d8d1d05..0000000 --- a/tests/Fixtures/Saloon/exportMail.getBudgetsExportMails.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjlQQ2VuWkxSNWdiVE9xOEpwYTEvS3c9PSIsInZhbHVlIjoiZTNvMDBlcUZqQUVIZmlZMmhnUW9ISjg5QnVkY1c0OE9mdWgwc3UzMUlrZS9uUlpnNTFBNHA1a2o4RmllTk5ON21rMmZkOXcwSkYvazg3SG40Rnc2UEpESTd2RnZSUTl3cVMwTFUwMWRWWWlCRTRPUTdscXJPL3JSNnB6RDdmNHAiLCJtYWMiOiJiZDljY2ZiYTRmYzUxNGQ3MmJhMzJhN2E3YWU3N2M3ZTVmMmY2ZDFhYTI2YzcxMTM4OTQ5MzQ5OWUzZDljY2QzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkxhbG9OQ2t3LzR4S3RGYko1SS93Zmc9PSIsInZhbHVlIjoiM1lQY1pMZGdQN1JhVzYzRW9wY0hpTUw3azFabW1taWRyRk1yRUVUOHJ3VmtoK293QTJ3OXE3N0ZzenFGOWZyMGZBZHEyRmtRc21nRmpCNGxzVlNHYXJ0M1FZUXhDdWtxd2VucVhKMjRvMC9paExhajdidmJMUDFaUDUwS0NzeUciLCJtYWMiOiIyNTUzYTEwNzA2YWM0NTQ4MWJlNDQ0MDUxZDhjZDk4NjRmOWJjYWFiYTRhMTExMDBmMjkxNmJlNmZiNjRiYjk4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/incident.getIncident.json b/tests/Fixtures/Saloon/incident.getIncident.json deleted file mode 100644 index 27a270a..0000000 --- a/tests/Fixtures/Saloon/incident.getIncident.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjAzNmxTVjVXM2psN3ltWXpZWUl5MkE9PSIsInZhbHVlIjoiQmptUk1jZmZBOGN2dmVlejd6N0QrMStNeDU1b3p2TjlBaUU1anlOMTRXcWVlSXJ2bE9IVEVyZkg4R2ZOOUxOZDJUSldFT2J3bXJRVFlpT2ttemYrc0JTOEw4SEdFNHVCMGErRU1CVDMxcXpNVi8wUWdOUXlFSWZ6SEQ4RkFnSnQiLCJtYWMiOiI0MzgxZTIzY2RmNDNiYTUzZmQzMzY3MWViM2U0YmRkMWNiM2YwN2QyNzg4YWMwNWEwNDlhMTUyNTIyZjc3YzY3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkU1WUF6b0NzTzZpS1FiMnVKVWZpdFE9PSIsInZhbHVlIjoiNkhqaWk1ZFFZeGZhOWRrNmpOdHhTaGZHQzlXT2J5ODlGaVFwSGh6UU5MRkZxeDA0cHFLMEc4QkZmcHZzUzlvRGs4ZWtNOHowTk44MmdjRy9oMzBTQlhvQUF2b29vN1J4Y1Z1TzVPK21lVmloTFhYV2pwdWZ3RXhQMTFkVkMwdnoiLCJtYWMiOiIyNWU5MGY1ZTM2NDRlOWFkNmVjM2YwYzlkOTk0ZGQ5ZGJlMTJiNGRmNjUzMTRhZGMwOTJiM2I3Y2YxNGIzMmEyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/incident.getIncidents.json b/tests/Fixtures/Saloon/incident.getIncidents.json deleted file mode 100644 index 59ade74..0000000 --- a/tests/Fixtures/Saloon/incident.getIncidents.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Im5lUWwyZk4rQzFMTW0vdUE1dEVtRUE9PSIsInZhbHVlIjoiYVhXN0tockhLamhkQ3VPWTZ6NkNSWm40YkYrT0ttQjZURUdZTFR0VHhkLzQycXIwZkNiK1d2eWw2L3dDRzhTQmV4V3hYWFFUelhmVmdaU2dMQS9UUHhlcFdHKzJMWlcybWJNanY3SHVQbkxndzAyei9YdHJnUjZtOEZWbkhiajAiLCJtYWMiOiIwZDYyZTdmY2QwOTA1MGJjNWE2ZjZhZmUzMWUwZTA0NjE0ODhhMmIxMzJiN2JiMjM2ZDYwYWM0ODA5OGE4NzEyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InRGVnJtRjVoOHNkcWNkYnQwNjV6UGc9PSIsInZhbHVlIjoiZ1EyQ05FYk5wTWRoZUZZOW5DQjl3em1NMGpxRno5aDdpeUhqam1hRUNTdXBrSFBSbHpFSGVDMlFueG1CUDFXY3lGWGtZNnBydHJUaE80UVhBSitPSzRuajV2NkN3Z1lsc2k5V3lqWUg1V3hodms0UG5tV2VUbUJJeGtzTSs2cFoiLCJtYWMiOiI4OWY5MDcyZWZmYTAwODZiZmVmNDBiMDJiOWUyYmRlMzU0YjIyOWMzODljODNkNTdiNTcxOGYwYzM0NTY0YzUyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json b/tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json deleted file mode 100644 index 41fc810..0000000 --- a/tests/Fixtures/Saloon/markAsExported.postOvertimeMarkAsExported.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlRJUVlZWnRVbUVmaldCZWQraE43eEE9PSIsInZhbHVlIjoieVE1dWYwUEZ1MEJ6ZUxwd1E1SnpuTkoyK1Jadm0vbll0N1J2VGdUOUxWQVg0YmVKSU41THZ2VG94a3J3d2EwcUIvQlV0bVJpR2MrVDYwQkhnNVA0V3VMYktqd2pEZFFaWi8xeHNkUHVYdGl2cEMvaHJaT0lSU3dFaVYrNGltMloiLCJtYWMiOiJlMTg0M2EwZDZhYjQzZTU2MDM0ZWFmMjAxZTZmYTZjMzVkYTZhNmMyZjE1MzI2ZjAyODQyYmM2ZGYyYTNmODNiIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6ImtGQW4vTzBrWllSQ2M4Q0R2b096ZEE9PSIsInZhbHVlIjoiTG5USUlIRFZRdEZzOGY3ZkI1dGwxTEdIWktDVjJQK21IMmNOaHBhUW1CNjNtVm5abW8rQzEvVzJ5b0JxUmRtdVJlZXVSUXp6RlE4RFRCMDVFSnZMakovYUc1NTdzK1lMalFpTFR4aksvUDJkL1JxZzFjWnlGUWcrVHc3QUZFVUMiLCJtYWMiOiIyYWNlYTc2NTk0MzFmY2I0MDA5YTlhMTRlNTliZTJlNTZkYTJkNzU1YzM1OTViOTAzMmE3ZTA5ZDIwNmM1MmU3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json b/tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json deleted file mode 100644 index 8f6e1c3..0000000 --- a/tests/Fixtures/Saloon/markAsInvoiced.postEntryMarkAsInvoiced.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Ink2d0M4aWFHOHFmT1JyWDY4L3k3RFE9PSIsInZhbHVlIjoiS2V0cU95bkV2cTNGR21ZOWlyRjdqSTNUVkJza3RYeldvWXUwVU45TnJKbE9Ta3JRbmtFbG00Zm9XcUVlVHUrUERjWjdSdlFPbHpUdExaQnArQ3A4TXc3REJTVmdwT3dqQktTQ2hFelhkSmRVM3pndGExVFBtNy9UbHp2K2ZjTXkiLCJtYWMiOiI0ZDVhNzkxOGNiNDNmMTQ0ZjFkZGVlYWYzM2YwOTlmODg1Y2QxZjQwNTU4OTE0YTA5YjFhYWRiNTU3YTA4ZjRlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkxFNzM2V0NkRHRTMVFkZURWbnc5dHc9PSIsInZhbHVlIjoiNTlyamhHSXloN0FXWWQzOUpUYTFUank0dmxaN3V4cXV3S3JCUHZzVnZOdS94Q29qWDVWU1hQQVdYd2lEYkxiMUI1WHczeStQMWYwWVV3VWZUY0o0R1FqSFcrelRZd2tpdVViSHg5L29tUGR6c2NlMHQ0T29GMkFZdVZQZ3pRZVciLCJtYWMiOiJmNTNmMzcyMjRjOTY4ZWNlMjg3OTUxNTZjZThjZGY1YWU4ODUzMmJhOWQwODY5YzI1M2JkY2ZkODg4MjJhMTg4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/me.getMes.json b/tests/Fixtures/Saloon/me.getMes.json deleted file mode 100644 index cffe264..0000000 --- a/tests/Fixtures/Saloon/me.getMes.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6InJEeFpzdmgvc1VjL1BBT0xuVmlOeFE9PSIsInZhbHVlIjoiVzRtL2FGZE16U3RrVE5vZ2oxOVdRUFlFQTZvVXNOb2VtdEVSMXU4V0hNaW96TUJNRmVJVTJoNnBsKzlrZWxwUnJtZnJVZmxJOTBmZlNlRllROWxjRjJ6N0ZMTUljRXl6OXRxVUdobHQrY0c2bzIzVUFDV0RLRnZEcnBpRFRGSmIiLCJtYWMiOiJlMDg2MjI0YzRlZWUzNmJlNGNkM2FjODQyYzZkNWRhOWMyOThhMmM4YmFlMjliMmIwYThiNTRlNDcwMGUwNzJmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkFsRjhZMkxtUEhsU0FTZnIwN09NMEE9PSIsInZhbHVlIjoiQkRXeE85SGhBeVNXWW1Qb3hxbUN4NzI5V0VyeWt2UkJaQ3k1ZU5QSkNOMDRlUXhHaGpTUFJFNXFaVHlFUnJEOHp3dTAvc3o2RTBCY0R4UjdkdDIrUGM4OG43VXF1dExvME5wc1I1Q3lPcXhRRUdENkFiUzlzRitkT3V6MEVPZHoiLCJtYWMiOiJkYTU1YTJlODczNGVkZWU2NzY4Y2I5YWFmYjE2NWE2MGNkNWM3MmZiOTFmNmM4Y2M2ZjhmMjEwNzgzNzgxNTQzIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/number.getIncidentsNumber.json b/tests/Fixtures/Saloon/number.getIncidentsNumber.json deleted file mode 100644 index e471153..0000000 --- a/tests/Fixtures/Saloon/number.getIncidentsNumber.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:16 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlpPY3doOUxIb1d4Vm1MMEZCSnFTdnc9PSIsInZhbHVlIjoibm9sVHBKMzJ0S2xwbjV3Q0FwenUyOWd6K3RlbGQvRk1JL2RUME9xTUhXT1NRUmtST3hKV2hPTnRkNkR1SUpiK0ErU0U5TGNIWGhhY1RLd1ZpTjN2dEVvMXNXcHdQd3d2RzM5TkE2THVkZWVvOWt4aGVSQnkzTFhXU3VMUW9BRVgiLCJtYWMiOiI0YmRlMDQzMWE0YjdiOTFhZjRkMmViMzAwOTJhMzkzOWMzZjQ0OGFiNzcxMDUyM2Q5YTFkOGRjMDQ3ZDE3M2EyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InQ2L1dRQjVRMDBXaUM2N1NLblZpcUE9PSIsInZhbHVlIjoiVkZpdG1hNlh1S2tDUGhDcWpkTURXTWNReTEvNStrZGhpQkV5QTJhUE5adytMTHdBQUoxaUZPdU5MMC8vZGl0ai8wL0JxOVZNeTZ0RjUzRUg0V0x3ZmM2bEw0TWR4WDEyVWNsdEcxQS9vYlFlTDhyaStSSENIUWxUMnhKNkZLYk4iLCJtYWMiOiIzZjI1ZGM3OThhNDEwNDk3MmRlY2NlODFjOTc5NTVhODJhNmQ1NjVkMzgxMDBmYTZhZGVkYzFkMGZhMDBiZTg0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:16 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/overtime.getOvertimes.json b/tests/Fixtures/Saloon/overtime.getOvertimes.json deleted file mode 100644 index cd9923e..0000000 --- a/tests/Fixtures/Saloon/overtime.getOvertimes.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6Ik5ia3pQa2NmMDFHSVJSbjBXZWIxK0E9PSIsInZhbHVlIjoicm92bEdXWkd4RjRvT3FVekVrQnNWSnVsL05JWmN3SFpoMFEzdXpMRC8yK0JicUg3RWRKSEhsdkVlUEV5TVk1S3VRQk5TdFJJZW11SkU2NnIrR0tqcm9LOWM3Q2FtWm9abXRxaFZpK0lKbm5NYndXUlVnWVhvc2xLdXoyczFZanUiLCJtYWMiOiI3OGQ4MjhjNzk4NWQ1NzhhNGQ5M2I1YmUzOGQ4YmNlOWMwOTI3ZDk0N2MzNzgxMWE1NjVhZWQ3MjFmNmQ0ODc4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlY4cTNEWCt5b28rYXNEWXZCTWJvMVE9PSIsInZhbHVlIjoiVkFuNjhwQXhwaHdlbDY4Z2oxRzBnVWVuSi9qTGdaSWdDRkZEM0IzWEszV0lwVm90NUFFY2w5NUVFcXU5T0trajd6UEUyUUpHY01DdmZXWWhMcDEzYk5vbnR6R0FTRXpuUkRqN0N6V3M5eGJMNzc5V015MWlIUXRBd3luQ3JwSUwiLCJtYWMiOiI2YTBmMTM0MTQ2NGU3ZGU3MGRlZTFlZTA2YTQyZDM1MDA4ZmNkNWZlMzFmYjA2Mzk1N2RjOTY1NjYzYjdkNTlkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/period.getBudgetPeriods.json b/tests/Fixtures/Saloon/period.getBudgetPeriods.json deleted file mode 100644 index ce07d96..0000000 --- a/tests/Fixtures/Saloon/period.getBudgetPeriods.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImpMSkR4WWJJeDNhakhvdUJLSDg0MlE9PSIsInZhbHVlIjoiaElHOHJWK0wwR1V4ODkrWklucmhPVnoxcUZENkJlWkVTSjFlMThKZGVJVlQ3S1lMUWh5c2pEQ2ZvdlNNTnYzMmpQMFlqZFRCdy8xNEVnY2RDRFlsRzcyalJRYmhKZUFEaU5MdkJpbFJkMkNYcU9oM09UcTdtUlFQOTI5dXNVa0MiLCJtYWMiOiI2MDEwYzAwNTA2NDE2MjE1ODZkZjkyMjgxMmQ3N2U4ZWNhYTE4ODQ4ZTE3Zjg3ZTYyZGZmMTc1MzY5MjNhODJkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlNmRVM3ZzB3RFpXdTFSSUJtcXJhQ3c9PSIsInZhbHVlIjoiYzg5SW81V3hoVDB1VE44UVhDMzQ2WUVWVTRkUldYN3AxbmhwVFpZNnFGM3RCeUdqODhHaVV2NENzVUJvSXh1eTJTendDV0swVGEwTlNYQzR2Mm5PK2tPQjhUcGRXYllMTnpmczBhdkowRFhUVk5hODdSa1hTdFJSWVJCaU1tZzAiLCJtYWMiOiIzYzVlNGI1NTEwOWU5MGNhNDNkM2ZjNzA2ZjBkNzliYmVjNGI1NGYwMmM0NTlkNGJmYzYzZGRmMTViYTIyYWZmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.deleteTeam.json b/tests/Fixtures/Saloon/team.deleteTeam.json deleted file mode 100644 index 9b81cb4..0000000 --- a/tests/Fixtures/Saloon/team.deleteTeam.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImhIcFNxcE8vdVl3c21VYWs3NmU3ZFE9PSIsInZhbHVlIjoiaUNEMlVSUmNza3NHb0dGQ0c0MlRNM3Bjd0pKTVVrZzN4Z0JrSzVUQWZWb2pTYnZhTVREWWxsTjR0bjYvYzVkKzdRcWJQcUtZZzVlcGdLN0V4VG9BL0VUODRRMW41UEdFbVRRMmw2MUdXRXFLdlpiWGFLQ2FSRUUxVzUrQ3pwWEEiLCJtYWMiOiI4YTU3ZmVmYmFmZDFkNmExYzU5YzEwZjE5ZGUwNmNmYTI0ZDRkMGI0NjgzYTQzNjNhYmQxYjI0OTM2OTMxYjQxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IkptdlRvbkpLT1g0NkNiZGZhM3lmSFE9PSIsInZhbHVlIjoicVQ0NExScTFFL3M3cnZVWnpOcmdmVzFjdkZsS1ZMMnFyS3pVeXl1Qm9JWlhqTEMyUTdueVlYMEQ4eW8wdUxqRlEzbWh6V0VJdHJYVHJsemRURG1meTRXTVNEUlNCcHA4MzFYZkRJS3ppc2h4WVNEblNlSXlFOFhOREZWRmoyM3giLCJtYWMiOiJhMTU3MDE4ZDI3YTE3Yzc1NWI1MzY1Zjk3YTQ4Zjk1Y2NlZDVhY2Q0NTA5OTI1MDhiMzM3MzMwOWJkYzE0MTlkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.getTeam.json b/tests/Fixtures/Saloon/team.getTeam.json deleted file mode 100644 index 8abbb57..0000000 --- a/tests/Fixtures/Saloon/team.getTeam.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6InV4Rk15enM0VlNFQ01sZXUxcVJMU2c9PSIsInZhbHVlIjoibERyZXZ5YkVMNHpvZDJ2eTdhZ3RlYUlHM1BhMUQ4bWtuR1JaRVNmb2lXUmt2Tjh3UVRHcmVzNjI5VXQvQVBsN0NWRlFIQ1pZL2RmOVA2VzlhdjZYdHJ3M3YxeEJVOTREZlc1TUpHMkF0b0djYW9tZFhzL0xlRzRMYTJtNys4V3QiLCJtYWMiOiJhODdhOGE2Mjc3YTJiY2U2Y2U2MTExOGNlMGI3YzhjODZhMjAyNTJkMmY4NzhiMjUyNzdlMmJkMzNlMzkxMTM4IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InFkNDFEQk5ZZVUwR0RqZXpGMzZnMVE9PSIsInZhbHVlIjoiek1qY2ZqbTRwdDVoN0pCcW9mckVpb0ZHTzJod3lpTFJpSHN6NG8vMGJDRG1CMlJjc3prdHdTYmRZemtQa3dtU2xzUFd3TVB0YzUyajRwb09uTWlHSFFMU1NiaVk4S3padE5tU28zWk9GUmdsdXRKd1QzeGY4Mmsra2U3NnNYZ2ciLCJtYWMiOiJlZTIwMzdiN2NiYTIwN2U1ZTZlMTUyYTNjNjZjZTdhNTVlMDFiOWRiNTU1YmUzNDc2ODk4MmMzNDBkMzA2ZDBlIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.getTeams.json b/tests/Fixtures/Saloon/team.getTeams.json deleted file mode 100644 index f5cb52c..0000000 --- a/tests/Fixtures/Saloon/team.getTeams.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6InJjcjh4bHkrZm1Na29HWWw0ZUJEakE9PSIsInZhbHVlIjoidS9zRVRLSm9CallLZkR3dE4vbnZjK0I3ZzRJT3IzZGozdXpnL0IrUDVBc3FEek1kNFZqOG4yZFhzN1hmL3dYalFsVXFQNU11aTU1K2V2b0d5ZWlZTUt6YTdMQnozWHJYWFdtMGdQREFreHJBK1dNN2JEdW5HcFA2OFVhTTcwR0MiLCJtYWMiOiJmODYyY2QzNDAzOGRhY2VhMjQ0MDMzZGZhZDZlYWQ1Y2ZhNzU2NDlkODg3NzYzYTFhNjYzZjk1YmEyN2YwMTQxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlB5SHZadXY3UzRFZWFqdmRBM2NlZFE9PSIsInZhbHVlIjoidllyS1JYNlZkaG5LeW9ZOWdlcWlESGh3RjlTSEZWYlhldzBwZUd3RHVuNzY0RFFtNkxpRnVsMmZPbjZTK0Z5L0IxNWFsOFVCdDY4ODY3WU1aNzc4MlhWZEtZYWZHa2VBL3VoUVVmQTJmbnRxdzlMK3B3Z1pmanEydCtzcXMyM04iLCJtYWMiOiJiZTRmYzM5ZWQ2OTFhMGVmMzg3ZTlhZWEyYzhlMzU4OGVhZGY4MDU2MTdjYzEwMDg2YzcwYmU4Y2E5OWM2YTk1IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.patchTeam.json b/tests/Fixtures/Saloon/team.patchTeam.json deleted file mode 100644 index 9995d8e..0000000 --- a/tests/Fixtures/Saloon/team.patchTeam.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImtWbG5UQSt4bkQvTU1GaUM2Wng5Nnc9PSIsInZhbHVlIjoibkZndVZhQSt5Smwra2pidDA0aCtITjF2RStaZjlDT1dtU0ExTVg3UkQ3dTNncytPM3dvUEhvbUpVTVAwS0Y4WFI3M0tjNGV2alFhRTBvYnNCNkl3cE90RFNPOHFzMTVVUUc2MUdRRGU0ekFtMTFDTGVua3NYUEpZQ3pjTlZxbDMiLCJtYWMiOiJjNWViNDE0OTg4ODUzODcxMzJlYzU3OTY4NDA4ZDZjZTZlMGJhYTg0ZWE1NWZlMDg0ZGUzZjVlMWJjZjM3ZjVmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InZVZ2t3OUk5M3ZLdldkWjRZblRKTkE9PSIsInZhbHVlIjoiY1F0aG8yOWF3OHI1ekhVTWU1QmlocUg1Zy9zNk12Q1ZPbUtQVFZmbkovU25GQTRNUndzSFdSZENBL0FTb0dLNnkzREtobER0UzBvb2dqRmVXbEFPSTBSeWhXUjB2YkRrdzFQU1lBd1krK1d3RzFUNWM3Q0Q4VFRFT0lQb3d5ZmEiLCJtYWMiOiIxZmM4YjEzYzQ1Njg0YTY1MDI5YzFmMjE4Y2RhMDY0YmU2YzU4ZmRmZGYxOTNiMDg2MWQ5ZDFkMTdjNWRiMTZhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/team.postTeams.json b/tests/Fixtures/Saloon/team.postTeams.json deleted file mode 100644 index f979dbd..0000000 --- a/tests/Fixtures/Saloon/team.postTeams.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImQvV1lpdFlHUkFhYU9hbS9XczE3VlE9PSIsInZhbHVlIjoia2J2SWFRQ3piT0pNZ0YrakllaFhkeGhjQ2tzOFdjQ0pHZXJ2TnVQVkRDeFJ4cFMwR1IraDNYemFaT2NmRkp6MkVSZE1SaTVuVEpIbi9VSEVEQ3Q5bStQb0VzL25EQ3pGTGFZVTUzcEZadVNDMldoUHRiUDRQb0lsT2U5QklhZkwiLCJtYWMiOiJiZjA5MTZhM2Y4MjZhMmNjZTY1NTZlYjEwNThlZjliYThjNzBkMjg0YmE2OTk4ODUxNjM0MDA0NWQ1ZjYzYmEyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6ImVTWGViR0hWSmU0U2pHMHA5cy9JcGc9PSIsInZhbHVlIjoiWWV3d3c2bk53aUg2TzBpaERRN1RYZFpTQXRiZ3RrUUhSZU54OU03QWFMWWNXV3YrR1Q2enMzM2w1bVgwVTBIK05Qc2FaMEVFVEhKT0FOcEVwVW1CcURtUHlhNlJ0Y2JXQy9aRkVKSkY3TjFkeDFyUHBES2VZaFhtSTR6aEFhNkIiLCJtYWMiOiJjNmJmYWM5YzNjN2I3OTY1M2RlMzUyNTRkNDhlMjdiN2NmMDU5ZTU4OWU2NjdmMGZjZjlkY2Y0YTc1YWY5NjE3IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json b/tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json deleted file mode 100644 index c8ddfed..0000000 --- a/tests/Fixtures/Saloon/timeSpentTotal.getTimeSpentTotals.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6ImFxK0ZNSE85aFBna1VERGtCeGFmb1E9PSIsInZhbHVlIjoiakNlZVlpK3cxOUM4Z1FWKzZRNm1zZ0k1Zndad1EwRzZaeDROQkt6YUk5VVpuREp3MHBXVHFSd000a0ZTc3R4anJWNVg2YkE0UnhCby9CNWFTZk42dytoaGZLYk5PSmI5Z1RNNHZ6RDN0NnBiR3B1Rm5WeEdhQWVvekNqUkFTdzkiLCJtYWMiOiJjZGZmMjljMWQzM2I4M2Q5YmI0NjA0MzI3MWIzYmQzZjQ0YTE3MTIzODM3ZGYyMGY3NmQxZDJiMjFmNTY5MmFkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Ikt5VmtFSjdqbVdMU2hac1hRaWw1K0E9PSIsInZhbHVlIjoiTTBVQ3FHSTJsU3d6Rm5QeVdVUGVLdGdleXM5M2Z3cUVpRXBjcEU4RmJWMy9lS2FoMDJsMDYxTUlVNXUwb3JPUmxST2FYaEtUWkJ0QnZrdVhRZ1RmRm93Vzd6aWN0MlRMemhYaVlnYmVKeEMrVEV1QXFBZWRFdlFrejdacytXMkoiLCJtYWMiOiIzYmNkZWVjYmRmZWMzYTc5MWI5OGIwNzk2ODg0N2I5MjgzZjFkMTlmYTI0MWQ0YTkxM2VlZWFjMjA1YzY3NWVhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.deleteUser.json b/tests/Fixtures/Saloon/user.deleteUser.json deleted file mode 100644 index 785982d..0000000 --- a/tests/Fixtures/Saloon/user.deleteUser.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjVyYkJmZHRWZG43WXFpbklhMHFSM1E9PSIsInZhbHVlIjoiZjZoVDRKcVI0Q1lSWU51OUUwa1NyOWk3Z0VsYUVQa2dodTh1S3FYRjFwaDc3OHRzVFR1Nm1nUXNnVzI1eHVVRzVDRkpZaXVjUWVDS051TGU5cDFmRDFVeFpQdGZEQ3FXRmhXVnhpQWNoWU43ZmZ0cUZ3RGJpd0wwUWdFeTlMbWgiLCJtYWMiOiI0ZWIzZTNkMzA5OTM0ZGU4YzYxOTMzMjE3ZDNiZWVmMTQ3ZGUxMWQxZjIyZWJkZWRlNTQ4YjYzY2FhMGFlNTRmIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IjFUZzRYSllLMG13VkJpUUR5OTBzNmc9PSIsInZhbHVlIjoicFNETjRRaDdpL3djVFVVQ2RXOU1ab3h5czNoM1V6ODZmZzR3R2JnMzA0ZXFPT1NrS2VOVkpTcjlIclBJT25mVU9FLzVGY0dWMVQ3SGYrb0V0WW92ZFM1b2lSMjh3QWxoZEQwUDM1bHZHeXF6V0YyUERJdEV3Y1ZkdUVjbFY1a2oiLCJtYWMiOiI0NmJkZmI4ZmRlNmY3OTBhZjA0ODY3NzMwZGQ4ODdlMmM0OGY4N2Q2YWJiOTQyMDIzMTA4N2I2MTQ4YTA4YTIyIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.getUser.json b/tests/Fixtures/Saloon/user.getUser.json deleted file mode 100644 index 8774f43..0000000 --- a/tests/Fixtures/Saloon/user.getUser.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6InVEek1Qc0dheGpVS2svR3BpelZBNHc9PSIsInZhbHVlIjoiazVuRkVhaU9GS3JRNkJrbk5rQi9VTi83bTh1OVJUNlFSVEpsYnJ1cWFVbGgyblZvRUcrdkRsTTBlS1pWZ2pma2tmYXVWdmRzY1BvcDFodXRIY2FGaTJHMlZGMzZyb21EN0FxS3lVcDVSTTUvMHVXNWlWTThEU2lVZXd0UElKcm4iLCJtYWMiOiI5YTAwNTU3MmEzZjg0NWU0NTIxNmY0MGE0MzEzNDBhNGJlOGFjMDkyMTI5YmU4YTQ2ZmMxMDE4ODY3NDQzMTI5IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IlBwSE94YlBHK1RrZWJBaWtIZWdzNGc9PSIsInZhbHVlIjoiR1llbGhUdXkyQVFDc04vOCtMUzZ0MC9PcXVwWWp2REtYb0p3V0JIRW9ZazNBYktNNWw3MDRzeTZwelJKOTVoZ2EyVGVrV3dQaHlnU2JoNjRsK2JxUTA1NmxrelFpRGdXb0hZOXVadFVHRjFkY1ZMZkxobTFVTUN4MHRTL09BMm8iLCJtYWMiOiIzNmMzMTQ0OWU0N2FlNjU0Zjc4MGI0ZDZkNjkxNjg3MjAwNDliMzczMWExNDBiYzY4OWQxZWUxMGMxOWVkOTQxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.getUsers.json b/tests/Fixtures/Saloon/user.getUsers.json deleted file mode 100644 index d0ddfb5..0000000 --- a/tests/Fixtures/Saloon/user.getUsers.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IkNzOUs1UmhhbE5vTFYycDNjcUdnVHc9PSIsInZhbHVlIjoiZXY4SnJpM2VzckZPd2F6akVEcWQ3OXJxVGV4MURHd1Iwakl4ZlA2ZTk3ZDFBYkpZQ3RnanVPVTJjcDlkay90ZjBVV3BZTE94di9rUmF1NC9McVZwdGZpUnJnSGpQSVZ2R2dUckZYNmRZalEyVXQxSjFzaU5IRWxTeEhIRDFGMTkiLCJtYWMiOiI3MTExMTBjNTc5YWQwMzc3NjM2MjFhMjM4NmY5ZDY1YjFkMTA2ZmUwM2UxNTI2ZjkzZTUyN2EwNjAyMGM4ODA2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Ik50bHA5SmI4Y3ZCTllnV0Yvcy96RlE9PSIsInZhbHVlIjoiMFI5bUliTmErL3hCNnlBUHQ2V2l3VWdSVzcrbHBIV0tvZlRWcTMyL0xRKytKa3U2dS9FR2Raekl6YWtxZ3lEZnhLWEZhZzY5SmZjSXptcDd4UkdWU0xaNzErQ1U4UFkyWnpBUmZIbTZOS0xoWk81eDFzelFJYmFPcCt3d3UveWkiLCJtYWMiOiI4Nzg4YzNkNTk2ZWRlYWY2N2RlN2Y5MThiOGM5ZGRiYTZmNGJlMzI0MTE2ZTE1Y2VkODM0YjYwMTgwNmU1YThkIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.patchUser.json b/tests/Fixtures/Saloon/user.patchUser.json deleted file mode 100644 index c3ee418..0000000 --- a/tests/Fixtures/Saloon/user.patchUser.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IlF0V04vR0pFN2ZFYmVYMFFlZ2xJd1E9PSIsInZhbHVlIjoiamFZdjBNblVnTjlWYmpUWjJmQUk0ZE9RNDZRWjUwQURFd3hmUldiZEVnZmZLU3dZTTJWMDZ3akF1UUh5MXZTNGpjcU1zU205RUJ1SXV4cGUxajJNd3hXa1lUWUVEa2hLUWFtWXVreVR4ZnpUVGc0UmVjWTZLM21CRjhqSG5vdkIiLCJtYWMiOiIwYmExMDZiMzBmMzBmNTY5MDc0MWQ1M2U1M2I1NzQzMjdiODgzN2MyZDA4OTA1ZTRlNjNlM2RiNTIzNjI3Y2FjIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6Ilc4UjZEMGRqbU4vOXMxbkJGZEk0S1E9PSIsInZhbHVlIjoiK0RXQURRRnNHemY1OGpsdzhnekExSnlhQUYzbWx4cUd1NTB0Ylo0UXNuUEY0YWZMQXRlMnBJMVdidm8yZk1BYUVOSFdhaS81VlFEenhXclhzSFhuREROeHh0bGJpdHAzamtKWTZORC9YMVJqUjBxaW5sK2hBQXBMYmp2c0dBcnIiLCJtYWMiOiIxZDA0YTI5MjNhMzBmYjQyYjQ3MWNkNjI2Mzg3NDA4MjlhMmFkNGVhMzBjOTY5NDdkODM4YzJhNDZiYmQ0Yjk2IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/user.postUsers.json b/tests/Fixtures/Saloon/user.postUsers.json deleted file mode 100644 index 388db2c..0000000 --- a/tests/Fixtures/Saloon/user.postUsers.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:22:18 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6IjU3ZDhYUnZRcmlVRytDTXozOWJZb0E9PSIsInZhbHVlIjoiTUQyY0tlZFQxQWtSMEJJa3ZtdG00Q2xpWXZNWDBuYVRLeWxnYTN0S0FjYUY2UG4yNGlIMWhWVmYxeGUrcHVSRmUzdFAxUUx0alpOZ3l0S2gxcWsrNFRDK0o5MExDTUxHSjFDbVpUejlLVzlHbWp5MDhpQURRVTlLT3FhaFNEWmMiLCJtYWMiOiJiZjMwOWRhNjRjMDcxYzcyY2IzMGNkNzg2ZDE1MWFiYTA2N2RhY2IyNmRiMWRjNDA5MGYwMGZlZTgyZGNjYzNhIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6IllVcmRhRW95OUMxdEtOQTZHa041RWc9PSIsInZhbHVlIjoid1g0YkN4OWF4RWJqdUhkTFhYb1JjMitSMjQzc1o4UXNmckhYU3ZacmJmY0Z1dTFzQ3kycUlSMm55TVlhUTBvTHVwM0VMOHZtdEN3cWtLYTByd3pGZlZjUnpNRE0rK2ZZZEJIL0pHRCtHSTBuN0EyVTlFaEFvelZXMWxiNitmYUMiLCJtYWMiOiJjNjgyYzY2NzVjNDE2MTRiMTdhN2NmOWMxMzk5NGJkMWYzMTY3ZWRjMzBlNDVhOGMxY2YyZDlmNTAyNGMzYjMwIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:22:18 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file diff --git a/tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json b/tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json deleted file mode 100644 index 59bdc28..0000000 --- a/tests/Fixtures/Saloon/userCustomerHoursAggregate.getUserCustomerHoursAggregates.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "statusCode": 401, - "headers": { - "Server": "nginx\/1.25.4", - "Content-Type": "application\/json", - "Transfer-Encoding": "chunked", - "Connection": "keep-alive", - "X-Powered-By": "PHP\/8.4.14", - "Cache-Control": "no-cache, private", - "Date": "Mon, 17 Nov 2025 21:21:17 GMT", - "Access-Control-Allow-Origin": "https:\/\/app.timatic.test", - "Access-Control-Allow-Credentials": "true", - "Set-Cookie": [ - "XSRF-TOKEN=eyJpdiI6InVZRmZkczN6cE52S1hxdGg2d2s3cGc9PSIsInZhbHVlIjoiaHhtdUJzOHg1OU0rMTA0VFFTV0M4YUZsRFVjdEhCQlJDV0ZpSDVHcFNHcjFzblhTcS9Hd0Q2eXhGSEQ1TVBqRXBTRmF0VG14VG9UMlY4Wmg4UWF0MVh2WXVSbjI4S0FNVGlBdHppMkpnU0RXK25tSzlkNlYrNHFtczlmcjYweXMiLCJtYWMiOiIzYmFjMGVhMDA3Y2MyZjY4ODY1ZGI1MmJlNGIxMzQ0YTI3NWZmMjZmZGFiZWFjMDhjYWVhYjc0OWViOThjNjA0IiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; samesite=none", - "timatic_api_session=eyJpdiI6InNENnM5Z2VLZm1QcmxOYlZjNis5VWc9PSIsInZhbHVlIjoiWDlNeW12Tkk4dEk0YWw1Q0QrNHB0ZFgvRlRnTkEwS051MjJWdkIxVUpTK3dsSGZ5eTdDckpac3hrWEhhWmRhY3Q3TmkrRUwzdXJUWmhlUU9oaGYyUmczRTNJakJMYmpYZmhpcE4vUVBnUzJGdGFBYTZqc0VMMHYvRHcvMDA0RkciLCJtYWMiOiJkOGM5OWYwYmI3YmFmMTdmNzI5ZWUyNzE0OWI0NjBiOGQwMjNkZTQ0ZGFlMWMwNjI0YmE2MzdmN2E3YzdjMjIxIiwidGFnIjoiIn0%3D; expires=Tue, 18 Nov 2025 09:21:17 GMT; Max-Age=43200; path=\/; domain=.timatic.test; secure; httponly; samesite=none" - ] - }, - "data": "{\"message\":\"Unauthenticated.\"}", - "context": [] -} \ No newline at end of file From 53a305122c61e1d377fc91c38ba040d9ed0e0c9f Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 00:03:33 +0100 Subject: [PATCH 22/62] add fluent filters --- .gitignore | 1 + claude-todo.md | 8 - generator/JsonApiRequestGenerator.php | 82 ++++++++++ src/Concerns/HasFilters.php | 23 +++ src/Filtering/Operator.php | 22 +++ src/Requests/Budget/GetBudgetsRequest.php | 45 +----- .../GetBudgetTimeSpentTotalsRequest.php | 13 +- src/Requests/Customer/GetCustomersRequest.php | 13 +- src/Requests/Entry/GetEntriesRequest.php | 141 +----------------- .../GetEntrySuggestionsRequest.php | 28 +--- src/Requests/Overtime/GetOvertimesRequest.php | 64 +------- .../GetTimeSpentTotalsRequest.php | 24 +-- src/Requests/User/GetUsersRequest.php | 13 +- .../GetUserCustomerHoursAggregatesRequest.php | 76 +--------- 14 files changed, 164 insertions(+), 389 deletions(-) create mode 100644 src/Concerns/HasFilters.php create mode 100644 src/Filtering/Operator.php diff --git a/.gitignore b/.gitignore index d36fd87..723f82e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ composer.lock .phpunit.result.cache .DS_Store openapi.json +.claude/settings.local.json diff --git a/claude-todo.md b/claude-todo.md index eaf91cc..23888a0 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -13,11 +13,3 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid - Custom response methods - Configuration options - Update regenerate instructies met nieuwe patterns - - -11. Vereenvoudig de filters voor de collection requests - -- Verwijder de filters uit de constructor van de collection request classes -- Bekijk de implementatie van de filters in de intermax/api-client package -- Voeg een filter() method toe aan de collection request classes in de generator -- Update README.md met nieuwe filter instructies diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index b11e98a..e9bc1cc 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -7,8 +7,10 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; use Nette\PhpGenerator\ClassType; +use Nette\PhpGenerator\Method; use Nette\PhpGenerator\PhpFile; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; use Timatic\SDK\Foundation\Model; class JsonApiRequestGenerator extends RequestGenerator @@ -52,6 +54,17 @@ protected function generateRequestClass(Endpoint $endpoint): PhpFile if ($this->isCollectionRequest($endpoint)) { $namespace->addUse(Paginatable::class); $classType->addImplement(Paginatable::class); + + // Check if this request has filter parameters before adding HasFilters trait + $hasFilters = $this->hasFilterParameters($classType); + + if ($hasFilters) { + $namespace->addUse(HasFilters::class); + $classType->addTrait(HasFilters::class); + } + + // Remove filter parameters from constructor, keep only include + $this->removeFilterParameters($classType); } return $phpFile; @@ -76,6 +89,24 @@ protected function isCollectionRequest(Endpoint $endpoint): bool return empty($endpoint->pathParameters); } + protected function hasFilterParameters(ClassType $classType): bool + { + if (! $classType->hasMethod('__construct')) { + return false; + } + + $constructor = $classType->getMethod('__construct'); + $parameters = $constructor->getParameters(); + + foreach ($parameters as $paramName => $parameter) { + if (str_starts_with($paramName, 'filter')) { + return true; + } + } + + return false; + } + protected function addModelDataParameter(ClassType $classType, $namespace): void { // Get constructor @@ -93,4 +124,55 @@ protected function addModelDataParameter(ClassType $classType, $namespace): void ->setReturnType('array') ->addBody('return $this->data ? $this->data->toJsonApi() : [];'); } + + protected function removeFilterParameters(ClassType $classType): void + { + $hasInclude = false; + + // Remove filter parameters from constructor + if ($classType->hasMethod('__construct')) { + $constructor = $classType->getMethod('__construct'); + $parameters = $constructor->getParameters(); + + foreach ($parameters as $paramName => $parameter) { + if (str_starts_with($paramName, 'filter')) { + $constructor->removeParameter($paramName); + } + } + + // Check if include parameter still exists after removing filters + $hasInclude = $constructor->hasParameter('include'); + + // Remove PHPDoc for filter parameters + $comment = $constructor->getComment(); + if ($comment) { + // Remove all @param lines that start with filter + $lines = explode("\n", $comment); + $filteredLines = array_filter($lines, function ($line) { + return ! preg_match('/@param.*\$filter/', $line); + }); + + // If only the opening /** and closing */ remain, remove the entire comment + $filteredLines = array_values($filteredLines); + if (count($filteredLines) <= 2) { + $constructor->setComment(null); + } else { + $constructor->setComment(implode("\n", $filteredLines)); + } + } + } + + // Update or remove defaultQuery + if ($classType->hasMethod('defaultQuery')) { + $defaultQuery = $classType->getMethod('defaultQuery'); + + if ($hasInclude) { + // If include parameter exists, only return that + $defaultQuery->setBody('return array_filter([\'include\' => $this->include]);'); + } else { + // If no include parameter, remove defaultQuery entirely + $classType->removeMethod('defaultQuery'); + } + } + } } diff --git a/src/Concerns/HasFilters.php b/src/Concerns/HasFilters.php new file mode 100644 index 0000000..ffcca44 --- /dev/null +++ b/src/Concerns/HasFilters.php @@ -0,0 +1,23 @@ +value.']'; + } + + $this->query()->add($key, $value); + + return $this; + } +} diff --git a/src/Filtering/Operator.php b/src/Filtering/Operator.php new file mode 100644 index 0000000..2e7e366 --- /dev/null +++ b/src/Filtering/Operator.php @@ -0,0 +1,22 @@ + $this->filtercustomerId, - 'filter[customerId][eq]' => $this->filtercustomerIdeq, - 'filter[customerId][nq]' => $this->filtercustomerIdnq, - 'filter[customerId][gt]' => $this->filtercustomerIdgt, - 'filter[customerId][lt]' => $this->filtercustomerIdlt, - 'filter[customerId][gte]' => $this->filtercustomerIdgte, - 'filter[customerId][lte]' => $this->filtercustomerIdlte, - 'filter[customerId][contains]' => $this->filtercustomerIdcontains, - 'filter[budgetTypeId]' => $this->filterbudgetTypeId, - 'filter[budgetTypeId][eq]' => $this->filterbudgetTypeIdeq, - 'filter[budgetTypeId][nq]' => $this->filterbudgetTypeIdnq, - 'filter[budgetTypeId][gt]' => $this->filterbudgetTypeIdgt, - 'filter[budgetTypeId][lt]' => $this->filterbudgetTypeIdlt, - 'filter[budgetTypeId][gte]' => $this->filterbudgetTypeIdgte, - 'filter[budgetTypeId][lte]' => $this->filterbudgetTypeIdlte, - 'filter[budgetTypeId][contains]' => $this->filterbudgetTypeIdcontains, - 'filter[isArchived]' => $this->filterisArchived, - 'filter[customerExternalId]' => $this->filtercustomerExternalId, - 'filter[showToCustomer]' => $this->filtershowToCustomer, - 'include' => $this->include, - ]); + return array_filter(['include' => $this->include]); } } diff --git a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php index c624f53..47d5136 100644 --- a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php +++ b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getBudgetTimeSpentTotals */ class GetBudgetTimeSpentTotalsRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,13 +21,5 @@ public function resolveEndpoint(): string return '/budget-time-spent-totals'; } - public function __construct( - protected ?string $filterbudgetId = null, - protected ?string $filterbudgetIdeq = null, - ) {} - - public function defaultQuery(): array - { - return array_filter(['filter[budgetId]' => $this->filterbudgetId, 'filter[budgetId][eq]' => $this->filterbudgetIdeq]); - } + public function __construct() {} } diff --git a/src/Requests/Customer/GetCustomersRequest.php b/src/Requests/Customer/GetCustomersRequest.php index 9930b80..50091bc 100644 --- a/src/Requests/Customer/GetCustomersRequest.php +++ b/src/Requests/Customer/GetCustomersRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getCustomers */ class GetCustomersRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,13 +21,5 @@ public function resolveEndpoint(): string return '/customers'; } - public function __construct( - protected ?string $filterexternalId = null, - protected ?string $filterexternalIdeq = null, - ) {} - - public function defaultQuery(): array - { - return array_filter(['filter[externalId]' => $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); - } + public function __construct() {} } diff --git a/src/Requests/Entry/GetEntriesRequest.php b/src/Requests/Entry/GetEntriesRequest.php index 652aa8a..ad65409 100644 --- a/src/Requests/Entry/GetEntriesRequest.php +++ b/src/Requests/Entry/GetEntriesRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getEntries */ class GetEntriesRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -19,147 +22,11 @@ public function resolveEndpoint(): string } public function __construct( - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - protected ?string $filteruserIdnq = null, - protected ?string $filteruserIdgt = null, - protected ?string $filteruserIdlt = null, - protected ?string $filteruserIdgte = null, - protected ?string $filteruserIdlte = null, - protected ?string $filteruserIdcontains = null, - protected ?string $filterbudgetId = null, - protected ?string $filterbudgetIdeq = null, - protected ?string $filterbudgetIdnq = null, - protected ?string $filterbudgetIdgt = null, - protected ?string $filterbudgetIdlt = null, - protected ?string $filterbudgetIdgte = null, - protected ?string $filterbudgetIdlte = null, - protected ?string $filterbudgetIdcontains = null, - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterhasOvertime = null, - protected ?string $filterhasOvertimeeq = null, - protected ?string $filterhasOvertimenq = null, - protected ?string $filterhasOvertimegt = null, - protected ?string $filterhasOvertimelt = null, - protected ?string $filterhasOvertimegte = null, - protected ?string $filterhasOvertimelte = null, - protected ?string $filterhasOvertimecontains = null, - protected ?string $filteruserFullName = null, - protected ?string $filteruserFullNameeq = null, - protected ?string $filteruserFullNamenq = null, - protected ?string $filteruserFullNamegt = null, - protected ?string $filteruserFullNamelt = null, - protected ?string $filteruserFullNamegte = null, - protected ?string $filteruserFullNamelte = null, - protected ?string $filteruserFullNamecontains = null, - protected ?string $filtercustomerId = null, - protected ?string $filtercustomerIdeq = null, - protected ?string $filtercustomerIdnq = null, - protected ?string $filtercustomerIdgt = null, - protected ?string $filtercustomerIdlt = null, - protected ?string $filtercustomerIdgte = null, - protected ?string $filtercustomerIdlte = null, - protected ?string $filtercustomerIdcontains = null, - protected ?string $filterticketNumber = null, - protected ?string $filterticketNumbereq = null, - protected ?string $filterticketNumbernq = null, - protected ?string $filterticketNumbergt = null, - protected ?string $filterticketNumberlt = null, - protected ?string $filterticketNumbergte = null, - protected ?string $filterticketNumberlte = null, - protected ?string $filterticketNumbercontains = null, - protected ?string $filtersettlement = null, - protected ?string $filterisInvoiced = null, - protected ?string $filterisInvoiceable = null, protected ?string $include = null, ) {} public function defaultQuery(): array { - return array_filter([ - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - 'filter[userId][nq]' => $this->filteruserIdnq, - 'filter[userId][gt]' => $this->filteruserIdgt, - 'filter[userId][lt]' => $this->filteruserIdlt, - 'filter[userId][gte]' => $this->filteruserIdgte, - 'filter[userId][lte]' => $this->filteruserIdlte, - 'filter[userId][contains]' => $this->filteruserIdcontains, - 'filter[budgetId]' => $this->filterbudgetId, - 'filter[budgetId][eq]' => $this->filterbudgetIdeq, - 'filter[budgetId][nq]' => $this->filterbudgetIdnq, - 'filter[budgetId][gt]' => $this->filterbudgetIdgt, - 'filter[budgetId][lt]' => $this->filterbudgetIdlt, - 'filter[budgetId][gte]' => $this->filterbudgetIdgte, - 'filter[budgetId][lte]' => $this->filterbudgetIdlte, - 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[hasOvertime]' => $this->filterhasOvertime, - 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, - 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, - 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, - 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, - 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, - 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, - 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, - 'filter[userFullName]' => $this->filteruserFullName, - 'filter[userFullName][eq]' => $this->filteruserFullNameeq, - 'filter[userFullName][nq]' => $this->filteruserFullNamenq, - 'filter[userFullName][gt]' => $this->filteruserFullNamegt, - 'filter[userFullName][lt]' => $this->filteruserFullNamelt, - 'filter[userFullName][gte]' => $this->filteruserFullNamegte, - 'filter[userFullName][lte]' => $this->filteruserFullNamelte, - 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, - 'filter[customerId]' => $this->filtercustomerId, - 'filter[customerId][eq]' => $this->filtercustomerIdeq, - 'filter[customerId][nq]' => $this->filtercustomerIdnq, - 'filter[customerId][gt]' => $this->filtercustomerIdgt, - 'filter[customerId][lt]' => $this->filtercustomerIdlt, - 'filter[customerId][gte]' => $this->filtercustomerIdgte, - 'filter[customerId][lte]' => $this->filtercustomerIdlte, - 'filter[customerId][contains]' => $this->filtercustomerIdcontains, - 'filter[ticketNumber]' => $this->filterticketNumber, - 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, - 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, - 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, - 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, - 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, - 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, - 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, - 'filter[settlement]' => $this->filtersettlement, - 'filter[isInvoiced]' => $this->filterisInvoiced, - 'filter[isInvoiceable]' => $this->filterisInvoiceable, - 'include' => $this->include, - ]); + return array_filter(['include' => $this->include]); } } diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php b/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php index 89b631c..2528e59 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getEntrySuggestions */ class GetEntrySuggestionsRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,28 +21,5 @@ public function resolveEndpoint(): string return '/entry-suggestions'; } - public function __construct( - protected ?string $filterdate = null, - protected ?string $filterdateeq = null, - protected ?string $filterdatenq = null, - protected ?string $filterdategt = null, - protected ?string $filterdatelt = null, - protected ?string $filterdategte = null, - protected ?string $filterdatelte = null, - protected ?string $filterdatecontains = null, - ) {} - - public function defaultQuery(): array - { - return array_filter([ - 'filter[date]' => $this->filterdate, - 'filter[date][eq]' => $this->filterdateeq, - 'filter[date][nq]' => $this->filterdatenq, - 'filter[date][gt]' => $this->filterdategt, - 'filter[date][lt]' => $this->filterdatelt, - 'filter[date][gte]' => $this->filterdategte, - 'filter[date][lte]' => $this->filterdatelte, - 'filter[date][contains]' => $this->filterdatecontains, - ]); - } + public function __construct() {} } diff --git a/src/Requests/Overtime/GetOvertimesRequest.php b/src/Requests/Overtime/GetOvertimesRequest.php index 48a1c85..fcbcb93 100644 --- a/src/Requests/Overtime/GetOvertimesRequest.php +++ b/src/Requests/Overtime/GetOvertimesRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getOvertimes */ class GetOvertimesRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,64 +21,5 @@ public function resolveEndpoint(): string return '/overtimes'; } - public function __construct( - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterisApproved = null, - protected ?string $filterapprovedAt = null, - protected ?string $filterapprovedAteq = null, - protected ?string $filterapprovedAtnq = null, - protected ?string $filterapprovedAtgt = null, - protected ?string $filterapprovedAtlt = null, - protected ?string $filterapprovedAtgte = null, - protected ?string $filterapprovedAtlte = null, - protected ?string $filterapprovedAtcontains = null, - protected ?string $filterisExported = null, - ) {} - - public function defaultQuery(): array - { - return array_filter([ - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[isApproved]' => $this->filterisApproved, - 'filter[approvedAt]' => $this->filterapprovedAt, - 'filter[approvedAt][eq]' => $this->filterapprovedAteq, - 'filter[approvedAt][nq]' => $this->filterapprovedAtnq, - 'filter[approvedAt][gt]' => $this->filterapprovedAtgt, - 'filter[approvedAt][lt]' => $this->filterapprovedAtlt, - 'filter[approvedAt][gte]' => $this->filterapprovedAtgte, - 'filter[approvedAt][lte]' => $this->filterapprovedAtlte, - 'filter[approvedAt][contains]' => $this->filterapprovedAtcontains, - 'filter[isExported]' => $this->filterisExported, - ]); - } + public function __construct() {} } diff --git a/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php b/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php index ec3a20b..892dc54 100644 --- a/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php +++ b/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getTimeSpentTotals */ class GetTimeSpentTotalsRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,24 +21,5 @@ public function resolveEndpoint(): string return '/time-spent-totals'; } - public function __construct( - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterteamId = null, - protected ?string $filterteamIdeq = null, - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - ) {} - - public function defaultQuery(): array - { - return array_filter([ - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[teamId]' => $this->filterteamId, - 'filter[teamId][eq]' => $this->filterteamIdeq, - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - ]); - } + public function __construct() {} } diff --git a/src/Requests/User/GetUsersRequest.php b/src/Requests/User/GetUsersRequest.php index cfff519..1cbc392 100644 --- a/src/Requests/User/GetUsersRequest.php +++ b/src/Requests/User/GetUsersRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getUsers */ class GetUsersRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,13 +21,5 @@ public function resolveEndpoint(): string return '/users'; } - public function __construct( - protected ?string $filterexternalId = null, - protected ?string $filterexternalIdeq = null, - ) {} - - public function defaultQuery(): array - { - return array_filter(['filter[externalId]' => $this->filterexternalId, 'filter[externalId][eq]' => $this->filterexternalIdeq]); - } + public function __construct() {} } diff --git a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php index d205bf8..9412e7d 100644 --- a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php +++ b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php @@ -5,12 +5,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Concerns\HasFilters; /** * getUserCustomerHoursAggregates */ class GetUserCustomerHoursAggregatesRequest extends Request implements Paginatable { + use HasFilters; + protected Method $method = Method::GET; public function resolveEndpoint(): string @@ -18,76 +21,5 @@ public function resolveEndpoint(): string return '/user-customer-hours-aggregates'; } - public function __construct( - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterteamId = null, - protected ?string $filterteamIdeq = null, - protected ?string $filterteamIdnq = null, - protected ?string $filterteamIdgt = null, - protected ?string $filterteamIdlt = null, - protected ?string $filterteamIdgte = null, - protected ?string $filterteamIdlte = null, - protected ?string $filterteamIdcontains = null, - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - protected ?string $filteruserIdnq = null, - protected ?string $filteruserIdgt = null, - protected ?string $filteruserIdlt = null, - protected ?string $filteruserIdgte = null, - protected ?string $filteruserIdlte = null, - protected ?string $filteruserIdcontains = null, - ) {} - - public function defaultQuery(): array - { - return array_filter([ - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[teamId]' => $this->filterteamId, - 'filter[teamId][eq]' => $this->filterteamIdeq, - 'filter[teamId][nq]' => $this->filterteamIdnq, - 'filter[teamId][gt]' => $this->filterteamIdgt, - 'filter[teamId][lt]' => $this->filterteamIdlt, - 'filter[teamId][gte]' => $this->filterteamIdgte, - 'filter[teamId][lte]' => $this->filterteamIdlte, - 'filter[teamId][contains]' => $this->filterteamIdcontains, - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - 'filter[userId][nq]' => $this->filteruserIdnq, - 'filter[userId][gt]' => $this->filteruserIdgt, - 'filter[userId][lt]' => $this->filteruserIdlt, - 'filter[userId][gte]' => $this->filteruserIdgte, - 'filter[userId][lte]' => $this->filteruserIdlte, - 'filter[userId][contains]' => $this->filteruserIdcontains, - ]); - } + public function __construct() {} } From 1bc215a6024e3bc60a48b9c5f873fc9cbc095554 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 00:03:49 +0100 Subject: [PATCH 23/62] remove timestamp from tests --- generator/stubs/pest-resource-test.stub | 1 - tests/Requests/ApproveTest.php | 2 -- tests/Requests/BudgetTest.php | 2 -- tests/Requests/BudgetTimeSpentTotalTest.php | 2 -- tests/Requests/BudgetTypeTest.php | 2 -- tests/Requests/ChangeTest.php | 2 -- tests/Requests/CorrectionTest.php | 2 -- tests/Requests/CustomerTest.php | 2 -- tests/Requests/DailyProgressTest.php | 2 -- tests/Requests/EntriesExportTest.php | 2 -- tests/Requests/EntrySuggestionTest.php | 2 -- tests/Requests/EntryTest.php | 2 -- tests/Requests/EventTest.php | 2 -- tests/Requests/ExportMailTest.php | 2 -- tests/Requests/IncidentTest.php | 2 -- tests/Requests/MarkAsExportedTest.php | 2 -- tests/Requests/MarkAsInvoicedTest.php | 2 -- tests/Requests/MeTest.php | 2 -- tests/Requests/NumberTest.php | 2 -- tests/Requests/OvertimeTest.php | 2 -- tests/Requests/PeriodTest.php | 2 -- tests/Requests/TeamTest.php | 2 -- tests/Requests/TimeSpentTotalTest.php | 2 -- tests/Requests/UserCustomerHoursAggregateTest.php | 2 -- tests/Requests/UserTest.php | 2 -- 25 files changed, 49 deletions(-) diff --git a/generator/stubs/pest-resource-test.stub b/generator/stubs/pest-resource-test.stub index de66ce9..5560edf 100644 --- a/generator/stubs/pest-resource-test.stub +++ b/generator/stubs/pest-resource-test.stub @@ -1,6 +1,5 @@ Date: Fri, 21 Nov 2025 11:09:20 +0100 Subject: [PATCH 24/62] add Id postfix to parameters --- generator/JsonApiPestTestGenerator.php | 16 ++ generator/JsonApiRequestGenerator.php | 215 ++++++++---------- generator/JsonApiResourceGenerator.php | 96 ++++---- generator/generate.php | 52 +++-- .../Approve/PostOvertimeApproveRequest.php | 9 +- src/Requests/Budget/DeleteBudgetRequest.php | 4 +- src/Requests/Budget/GetBudgetRequest.php | 4 +- src/Requests/Budget/GetBudgetsRequest.php | 2 +- src/Requests/Budget/PatchBudgetRequest.php | 9 +- src/Requests/Budget/PostBudgetsRequest.php | 5 +- src/Requests/Change/GetChangeRequest.php | 4 +- .../Correction/PatchCorrectionRequest.php | 9 +- .../Correction/PostCorrectionsRequest.php | 5 +- .../Customer/DeleteCustomerRequest.php | 4 +- src/Requests/Customer/GetCustomerRequest.php | 4 +- .../Customer/PatchCustomerRequest.php | 9 +- .../Customer/PostCustomersRequest.php | 5 +- .../GetBudgetEntriesExportRequest.php | 144 +----------- src/Requests/Entry/DeleteEntryRequest.php | 4 +- src/Requests/Entry/GetEntriesRequest.php | 2 +- src/Requests/Entry/GetEntryRequest.php | 4 +- src/Requests/Entry/PatchEntryRequest.php | 9 +- src/Requests/Entry/PostEntriesRequest.php | 5 +- .../DeleteEntrySuggestionRequest.php | 4 +- .../GetEntrySuggestionRequest.php | 4 +- src/Requests/Event/PostEventsRequest.php | 5 +- src/Requests/Incident/GetIncidentRequest.php | 4 +- .../PostOvertimeMarkAsExportedRequest.php | 9 +- .../PostEntryMarkAsInvoicedRequest.php | 9 +- .../Number/GetIncidentsNumberRequest.php | 4 +- .../Period/GetBudgetPeriodsRequest.php | 4 +- src/Requests/Team/DeleteTeamRequest.php | 4 +- src/Requests/Team/GetTeamRequest.php | 4 +- src/Requests/Team/PatchTeamRequest.php | 9 +- src/Requests/Team/PostTeamsRequest.php | 5 +- src/Requests/User/DeleteUserRequest.php | 4 +- src/Requests/User/GetUserRequest.php | 4 +- src/Requests/User/PatchUserRequest.php | 9 +- src/Requests/User/PostUsersRequest.php | 5 +- src/Resource/Approve.php | 12 +- src/Resource/Budget.php | 21 +- src/Resource/Change.php | 4 +- src/Resource/Correction.php | 13 +- src/Resource/Customer.php | 21 +- src/Resource/EntriesExport.php | 4 +- src/Resource/Entry.php | 21 +- src/Resource/EntrySuggestion.php | 8 +- src/Resource/Event.php | 6 +- src/Resource/Incident.php | 4 +- src/Resource/MarkAsExported.php | 12 +- src/Resource/MarkAsInvoiced.php | 12 +- src/Resource/Number.php | 4 +- src/Resource/Period.php | 4 +- src/Resource/Team.php | 21 +- src/Resource/User.php | 21 +- tests/Requests/ApproveTest.php | 2 +- tests/Requests/BudgetTest.php | 6 +- tests/Requests/ChangeTest.php | 2 +- tests/Requests/CorrectionTest.php | 2 +- tests/Requests/CustomerTest.php | 6 +- tests/Requests/EntriesExportTest.php | 2 +- tests/Requests/EntrySuggestionTest.php | 4 +- tests/Requests/EntryTest.php | 6 +- tests/Requests/IncidentTest.php | 2 +- tests/Requests/MarkAsExportedTest.php | 2 +- tests/Requests/MarkAsInvoicedTest.php | 2 +- tests/Requests/NumberTest.php | 2 +- tests/Requests/PeriodTest.php | 2 +- tests/Requests/TeamTest.php | 6 +- tests/Requests/UserTest.php | 6 +- 70 files changed, 457 insertions(+), 490 deletions(-) diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index f4945c2..cbbd7cc 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -5,6 +5,7 @@ namespace Timatic\SDK\Generator; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\PestTestGenerator; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; @@ -81,4 +82,19 @@ protected function getTestPath(string $resourceName): string { return "tests/Requests/{$resourceName}Test.php"; } + + /** + * Hook: Transform path parameter names (e.g., budget -> budgetId) + */ + protected function getTestParameterName(Parameter $parameter, Endpoint $endpoint): string + { + $name = parent::getTestParameterName($parameter, $endpoint); + + // Check if this is a path parameter + if (in_array($parameter, $endpoint->pathParameters, true)) { + return $name.'Id'; + } + + return $name; + } } diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index e9bc1cc..15c922b 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -5,174 +5,141 @@ namespace Timatic\SDK\Generator; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; +use Crescat\SaloonSdkGenerator\Helpers\MethodGeneratorHelper; use Nette\PhpGenerator\ClassType; -use Nette\PhpGenerator\Method; -use Nette\PhpGenerator\PhpFile; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; use Timatic\SDK\Foundation\Model; class JsonApiRequestGenerator extends RequestGenerator { - public function generate($specification): array + /** + * Hook: Filter out PUT requests - not supported in JSON:API + */ + protected function shouldIncludeEndpoint(Endpoint $endpoint): bool { - // Filter out PUT endpoints before generating - $filteredSpec = clone $specification; - $filteredSpec->endpoints = array_filter( - $specification->endpoints, - fn ($endpoint) => ! $endpoint->method->isPut() - ); - - return parent::generate($filteredSpec); + return ! $endpoint->method->isPut(); } - protected function generateRequestClass(Endpoint $endpoint): PhpFile + /** + * Hook: Add "Request" suffix to class names + */ + protected function getRequestClassName(Endpoint $endpoint): string { - // Use parent generation for most of the class - $phpFile = parent::generateRequestClass($endpoint); - - // Get the class and namespace - $namespace = array_values($phpFile->getNamespaces())[0]; - $classType = array_values($namespace->getClasses())[0]; - - // Add "Request" suffix to class name - $originalName = $classType->getName(); - if (! str_ends_with($originalName, 'Request')) { - $classType->setName($originalName.'Request'); - } - - // Add Model import - $namespace->addUse(Model::class); - - // For POST/PUT/PATCH without body parameters, add Model data parameter - if ($this->isMutationRequest($endpoint) && empty($endpoint->bodyParameters)) { - $this->addModelDataParameter($classType, $namespace); - } - - // For GET collection requests, add Paginatable interface - if ($this->isCollectionRequest($endpoint)) { - $namespace->addUse(Paginatable::class); - $classType->addImplement(Paginatable::class); - - // Check if this request has filter parameters before adding HasFilters trait - $hasFilters = $this->hasFilterParameters($classType); - - if ($hasFilters) { - $namespace->addUse(HasFilters::class); - $classType->addTrait(HasFilters::class); - } + $className = parent::getRequestClassName($endpoint); - // Remove filter parameters from constructor, keep only include - $this->removeFilterParameters($classType); + if (! str_ends_with($className, 'Request')) { + $className .= 'Request'; } - return $phpFile; + return $className; } - protected function isMutationRequest(Endpoint $endpoint): bool + /** + * Hook: Transform path parameter names (e.g., budget -> budgetId) + */ + protected function getConstructorParameterName(string $originalName, bool $isPathParam = false): string { - // Only POST and PATCH are supported mutation methods - return $endpoint->method->isPost() - || $endpoint->method->isPatch(); - } - - protected function isCollectionRequest(Endpoint $endpoint): bool - { - // Collection requests are GET requests without an ID parameter in the path - if (! $endpoint->method->isGet()) { - return false; + if ($isPathParam) { + return $originalName.'Id'; } - // Check if the path contains a parameter (like {id}, {budget}, etc.) - // Collection endpoints typically don't have path parameters - return empty($endpoint->pathParameters); + return $originalName; } - protected function hasFilterParameters(ClassType $classType): bool + /** + * Hook: Customize request class for collection requests + */ + protected function customizeRequestClass(ClassType $classType, $namespace, Endpoint $endpoint): void { - if (! $classType->hasMethod('__construct')) { - return false; + if (! $this->isCollectionRequest($endpoint)) { + return; } - $constructor = $classType->getMethod('__construct'); - $parameters = $constructor->getParameters(); + // Add Paginatable interface to all collection requests + $namespace->addUse(Paginatable::class); + $classType->addImplement(Paginatable::class); - foreach ($parameters as $paramName => $parameter) { - if (str_starts_with($paramName, 'filter')) { - return true; - } + // Add HasFilters trait if collection has filter parameters in the endpoint + if ($this->hasFilterParameters($endpoint)) { + $namespace->addUse(HasFilters::class); + $classType->addTrait(HasFilters::class); } - - return false; } - protected function addModelDataParameter(ClassType $classType, $namespace): void + /** + * Hook: Customize constructor for mutation requests + */ + protected function customizeConstructor($classConstructor, ClassType $classType, $namespace, Endpoint $endpoint): void { - // Get constructor - $constructor = $classType->getMethod('__construct'); + if (! $this->isMutationRequest($endpoint)) { + return; + } - // Add data parameter with Model|array|null type to match Resource signature - $constructor->addPromotedParameter('data') - ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') - ->setProtected() - ->setNullable(); + $namespace->addUse(Model::class); + + $dataParam = new Parameter( + type: 'Timatic\\SDK\\Foundation\\Model|array|null', + nullable: true, + name: 'data', + description: 'Request data', + ); - // Add defaultBody method - $defaultBody = $classType->addMethod('defaultBody') + MethodGeneratorHelper::addParameterAsPromotedProperty($classConstructor, $dataParam); + + $classType->addMethod('defaultBody') ->setProtected() ->setReturnType('array') ->addBody('return $this->data ? $this->data->toJsonApi() : [];'); } - protected function removeFilterParameters(ClassType $classType): void + /** + * Hook: Filter out filter* query parameters (handled by HasFilters trait) + */ + protected function shouldIncludeQueryParameter(string $paramName): bool { - $hasInclude = false; + return ! str_starts_with($paramName, 'filter'); + } - // Remove filter parameters from constructor - if ($classType->hasMethod('__construct')) { - $constructor = $classType->getMethod('__construct'); - $parameters = $constructor->getParameters(); + /** + * Hook: Generate defaultQuery method with custom JSON:API logic + */ + protected function generateDefaultQueryMethod(\Nette\PhpGenerator\ClassType $classType, $namespace, array $queryParams, Endpoint $endpoint): void + { + // If we have any query parameters (likely just 'include'), use array_filter + if (! empty($queryParams)) { + $classType->addMethod('defaultQuery') + ->setProtected() + ->setReturnType('array') + ->addBody("return array_filter(['include' => \$this->include]);"); + } + } - foreach ($parameters as $paramName => $parameter) { - if (str_starts_with($paramName, 'filter')) { - $constructor->removeParameter($paramName); - } - } + // Helper methods for JSON:API logic - // Check if include parameter still exists after removing filters - $hasInclude = $constructor->hasParameter('include'); - - // Remove PHPDoc for filter parameters - $comment = $constructor->getComment(); - if ($comment) { - // Remove all @param lines that start with filter - $lines = explode("\n", $comment); - $filteredLines = array_filter($lines, function ($line) { - return ! preg_match('/@param.*\$filter/', $line); - }); - - // If only the opening /** and closing */ remain, remove the entire comment - $filteredLines = array_values($filteredLines); - if (count($filteredLines) <= 2) { - $constructor->setComment(null); - } else { - $constructor->setComment(implode("\n", $filteredLines)); - } - } - } + protected function isMutationRequest(Endpoint $endpoint): bool + { + // Only POST and PATCH are supported mutation methods + return $endpoint->method->isPost() + || $endpoint->method->isPatch(); + } - // Update or remove defaultQuery - if ($classType->hasMethod('defaultQuery')) { - $defaultQuery = $classType->getMethod('defaultQuery'); + protected function isCollectionRequest(Endpoint $endpoint): bool + { + // Collection requests are GET requests without path parameters + return $endpoint->method->isGet() && empty($endpoint->pathParameters); + } - if ($hasInclude) { - // If include parameter exists, only return that - $defaultQuery->setBody('return array_filter([\'include\' => $this->include]);'); - } else { - // If no include parameter, remove defaultQuery entirely - $classType->removeMethod('defaultQuery'); + protected function hasFilterParameters(Endpoint $endpoint): bool + { + foreach ($endpoint->queryParameters as $param) { + if (str_starts_with($param->name, 'filter')) { + return true; } } + + return false; } } diff --git a/generator/JsonApiResourceGenerator.php b/generator/JsonApiResourceGenerator.php index c6743d2..ca72ff3 100644 --- a/generator/JsonApiResourceGenerator.php +++ b/generator/JsonApiResourceGenerator.php @@ -5,68 +5,78 @@ namespace Timatic\SDK\Generator; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\ResourceGenerator; -use Nette\PhpGenerator\Method; +use Timatic\SDK\Foundation\Model; class JsonApiResourceGenerator extends ResourceGenerator { - public function generate($specification): array + /** + * Hook: Filter out PUT requests - not supported in JSON:API + */ + protected function shouldIncludeEndpoint(Endpoint $endpoint): bool { - // Filter out PUT endpoints before generating - $filteredSpec = clone $specification; - $filteredSpec->endpoints = array_filter( - $specification->endpoints, - fn ($endpoint) => ! $endpoint->method->isPut() - ); - - return parent::generate($filteredSpec); + return ! $endpoint->method->isPut(); } - protected function generateResourceMethod(Endpoint $endpoint, Method $method): void + /** + * Hook: Add "Request" suffix to request class names + */ + protected function getRequestClassName(Endpoint $endpoint): string { - parent::generateResourceMethod($endpoint, $method); + $className = parent::getRequestClassName($endpoint); - // For POST/PUT/PATCH requests without body parameters, add data parameter - if ($this->isMutationRequest($endpoint) && empty($endpoint->bodyParameters)) { - echo "Adding data parameter to {$endpoint->name}\n"; - $this->addDataParameterToMethod($endpoint, $method); + if (! str_ends_with($className, 'Request')) { + $className .= 'Request'; } + + return $className; } - protected function addDataParameterToMethod(Endpoint $endpoint, Method $method): void + /** + * Hook: Strip "Request" suffix from method names + */ + protected function getMethodName(Endpoint $endpoint, string $requestClassName): string { - // Add data parameter to method signature - $method->addParameter('data') - ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') - ->setDefaultValue(null); + // Strip "Request" suffix if present to get clean method names + $methodBaseName = str_ends_with($requestClassName, 'Request') + ? substr($requestClassName, 0, -7) + : $requestClassName; - // Update method body to pass data parameter to request constructor - $body = $method->getBody(); + return \Crescat\SaloonSdkGenerator\Helpers\NameHelper::safeVariableName($methodBaseName); + } - // Find the "new RequestName(" pattern and add $data parameter - if (! empty($endpoint->pathParameters)) { - // Has path params: new Request($param, null) -> new Request($param, $data) - $body = preg_replace( - '/(new\s+'.preg_quote($endpoint->name, '/').'\([^)]+)\)/', - '$1, $data)', - $body - ); - } else { - // No path params: new Request() -> new Request($data) - $body = str_replace( - 'new '.$endpoint->name.'()', - 'new '.$endpoint->name.'($data)', - $body - ); + /** + * Hook: Transform path parameter names (e.g., budget -> budgetId) + */ + protected function getResourceParameterName(Parameter $parameter, bool $isPathParam): string + { + if ($isPathParam) { + return $parameter->name.'Id'; } - $method->setBody($body); + return $parameter->name; } - protected function isMutationRequest(Endpoint $endpoint): bool + /** + * Hook: Customize resource method for mutation requests + */ + protected function customizeResourceMethod(\Nette\PhpGenerator\Method $method, $namespace, array &$args, Endpoint $endpoint): void { - // Only POST and PATCH are supported (PUT is filtered out) - return $endpoint->method->isPost() - || $endpoint->method->isPatch(); + if (! ($endpoint->method->isPost() || $endpoint->method->isPatch())) { + return; + } + + $namespace->addUse(Model::class); + + $dataParam = new Parameter( + type: 'Timatic\\SDK\\Foundation\\Model|array|null', + nullable: true, + name: 'data', + description: 'Request data', + ); + + $this->addPropertyToMethod($method, $dataParam); + $args[] = new \Nette\PhpGenerator\Literal('$data'); } } diff --git a/generator/generate.php b/generator/generate.php index 96cc014..914db48 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -138,9 +138,8 @@ function writeFile($file, $outputDir, $namespace) } } - // Fix POST/PUT/PATCH methods to add $data parameter and update class references + // Fix class references: add "Request" suffix to class instantiations in method body foreach ($classType->getMethods() as $method) { - $methodName = $method->getName(); $body = $method->getBody(); // Add "Request" suffix to class instantiations in method body @@ -158,27 +157,23 @@ function ($matches) { $body ); - // Check if it's a mutation method (post/patch only, PUT is not supported) - if (preg_match('/^(post|patch)/i', $methodName)) { - // Add data parameter - $method->addParameter('data') - ->setType('\\Timatic\\SDK\\Foundation\\Model|array|null') - ->setDefaultValue(null); - - // Update method body to pass $data to request constructor - // Pattern 2 first: new Request($param) -> new Request($param, $data) - // Must be checked before Pattern 1 to avoid double replacement - if (preg_match('/\(new\s+\w+Request\([^)]+\)\)/', $body)) { + // Fix mutation methods that have $data parameter but don't pass it + $methodName = $method->getName(); + if (preg_match('/^(post|patch)/i', $methodName) && $method->hasParameter('data')) { + // Check if Request instantiation exists without $data parameter + // Matches: new PostBudgetsRequest() or new PostBudgetsRequest (without parens) + if (preg_match('/new\s+\w+Request\(\s*\)/', $body)) { + // Has empty parens: replace () with ($data) $body = preg_replace( - '/\(new\s+(\w+Request)\(([^)]+)\)\)/', - '(new $1($2, $data))', + '/new\s+(\w+Request)\(\s*\)/', + 'new $1($data)', $body ); - } else { - // Pattern 1: new Request() -> new Request($data) + } elseif (preg_match('/new\s+\w+Request(?![(\w])/', $body)) { + // No parens: add ($data) $body = preg_replace( - '/\(new\s+(\w+Request)\(\)\)/', - '(new $1($data))', + '/new\s+(\w+Request)(?![(\w])/', + 'new $1($data)', $body ); } @@ -205,7 +200,7 @@ function ($matches) { echo ' āœ“ '.basename($path)."\n"; } -// Write test files +// Write test files (also apply path parameter transformations here as fallback) if ($tests && is_array($tests)) { echo "\n🧪 Tests:\n"; foreach ($tests as $file) { @@ -218,7 +213,22 @@ function ($matches) { mkdir($dir, 0755, true); } - file_put_contents($testPath, $file->file); + // Apply path parameter transformations (as fallback if generator didn't do it) + $testContent = $file->file; + $resourceNames = [ + 'budget', 'customer', 'user', 'team', 'entry', 'entrySuggestion', + 'correction', 'change', 'incident', 'overtime', + ]; + + foreach ($resourceNames as $resourceName) { + $testContent = preg_replace( + "/\b{$resourceName}:\s*(['\"])/", + "{$resourceName}Id: $1", + $testContent + ); + } + + file_put_contents($testPath, $testContent); echo ' āœ“ '.basename($testPath)."\n"; } } diff --git a/src/Requests/Approve/PostOvertimeApproveRequest.php b/src/Requests/Approve/PostOvertimeApproveRequest.php index ae73914..7d57b41 100644 --- a/src/Requests/Approve/PostOvertimeApproveRequest.php +++ b/src/Requests/Approve/PostOvertimeApproveRequest.php @@ -19,12 +19,15 @@ class PostOvertimeApproveRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/overtimes/{$this->overtime}/approve"; + return "/overtimes/{$this->overtimeId}/approve"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $overtime, - protected Model|array|null $data, + protected string $overtimeId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Budget/DeleteBudgetRequest.php b/src/Requests/Budget/DeleteBudgetRequest.php index 5b0943b..b107701 100644 --- a/src/Requests/Budget/DeleteBudgetRequest.php +++ b/src/Requests/Budget/DeleteBudgetRequest.php @@ -14,10 +14,10 @@ class DeleteBudgetRequest extends Request public function resolveEndpoint(): string { - return "/budgets/{$this->budget}"; + return "/budgets/{$this->budgetId}"; } public function __construct( - protected string $budget, + protected string $budgetId, ) {} } diff --git a/src/Requests/Budget/GetBudgetRequest.php b/src/Requests/Budget/GetBudgetRequest.php index b0f2ccd..00da2a7 100644 --- a/src/Requests/Budget/GetBudgetRequest.php +++ b/src/Requests/Budget/GetBudgetRequest.php @@ -14,10 +14,10 @@ class GetBudgetRequest extends Request public function resolveEndpoint(): string { - return "/budgets/{$this->budget}"; + return "/budgets/{$this->budgetId}"; } public function __construct( - protected string $budget, + protected string $budgetId, ) {} } diff --git a/src/Requests/Budget/GetBudgetsRequest.php b/src/Requests/Budget/GetBudgetsRequest.php index 27e7211..ed269ab 100644 --- a/src/Requests/Budget/GetBudgetsRequest.php +++ b/src/Requests/Budget/GetBudgetsRequest.php @@ -25,7 +25,7 @@ public function __construct( protected ?string $include = null, ) {} - public function defaultQuery(): array + protected function defaultQuery(): array { return array_filter(['include' => $this->include]); } diff --git a/src/Requests/Budget/PatchBudgetRequest.php b/src/Requests/Budget/PatchBudgetRequest.php index 5eae0de..544d8c4 100644 --- a/src/Requests/Budget/PatchBudgetRequest.php +++ b/src/Requests/Budget/PatchBudgetRequest.php @@ -19,12 +19,15 @@ class PatchBudgetRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/budgets/{$this->budget}"; + return "/budgets/{$this->budgetId}"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $budget, - protected Model|array|null $data, + protected string $budgetId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Budget/PostBudgetsRequest.php b/src/Requests/Budget/PostBudgetsRequest.php index f553f9b..3fb7182 100644 --- a/src/Requests/Budget/PostBudgetsRequest.php +++ b/src/Requests/Budget/PostBudgetsRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/budgets'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Change/GetChangeRequest.php b/src/Requests/Change/GetChangeRequest.php index 29eee20..9cdb833 100644 --- a/src/Requests/Change/GetChangeRequest.php +++ b/src/Requests/Change/GetChangeRequest.php @@ -14,10 +14,10 @@ class GetChangeRequest extends Request public function resolveEndpoint(): string { - return "/changes/{$this->change}"; + return "/changes/{$this->changeId}"; } public function __construct( - protected string $change, + protected string $changeId, ) {} } diff --git a/src/Requests/Correction/PatchCorrectionRequest.php b/src/Requests/Correction/PatchCorrectionRequest.php index 394f778..2d019fc 100644 --- a/src/Requests/Correction/PatchCorrectionRequest.php +++ b/src/Requests/Correction/PatchCorrectionRequest.php @@ -19,12 +19,15 @@ class PatchCorrectionRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/corrections/{$this->correction}"; + return "/corrections/{$this->correctionId}"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $correction, - protected Model|array|null $data, + protected string $correctionId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Correction/PostCorrectionsRequest.php b/src/Requests/Correction/PostCorrectionsRequest.php index df0c3f5..34367f3 100644 --- a/src/Requests/Correction/PostCorrectionsRequest.php +++ b/src/Requests/Correction/PostCorrectionsRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/corrections'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Customer/DeleteCustomerRequest.php b/src/Requests/Customer/DeleteCustomerRequest.php index 1dd7c24..4d3677a 100644 --- a/src/Requests/Customer/DeleteCustomerRequest.php +++ b/src/Requests/Customer/DeleteCustomerRequest.php @@ -14,10 +14,10 @@ class DeleteCustomerRequest extends Request public function resolveEndpoint(): string { - return "/customers/{$this->customer}"; + return "/customers/{$this->customerId}"; } public function __construct( - protected string $customer, + protected string $customerId, ) {} } diff --git a/src/Requests/Customer/GetCustomerRequest.php b/src/Requests/Customer/GetCustomerRequest.php index 0d32f0e..36054ce 100644 --- a/src/Requests/Customer/GetCustomerRequest.php +++ b/src/Requests/Customer/GetCustomerRequest.php @@ -14,10 +14,10 @@ class GetCustomerRequest extends Request public function resolveEndpoint(): string { - return "/customers/{$this->customer}"; + return "/customers/{$this->customerId}"; } public function __construct( - protected string $customer, + protected string $customerId, ) {} } diff --git a/src/Requests/Customer/PatchCustomerRequest.php b/src/Requests/Customer/PatchCustomerRequest.php index 1ed43ae..42b28d6 100644 --- a/src/Requests/Customer/PatchCustomerRequest.php +++ b/src/Requests/Customer/PatchCustomerRequest.php @@ -19,12 +19,15 @@ class PatchCustomerRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/customers/{$this->customer}"; + return "/customers/{$this->customerId}"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $customer, - protected Model|array|null $data, + protected string $customerId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Customer/PostCustomersRequest.php b/src/Requests/Customer/PostCustomersRequest.php index ddff920..c94a5e9 100644 --- a/src/Requests/Customer/PostCustomersRequest.php +++ b/src/Requests/Customer/PostCustomersRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/customers'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php index c65e263..1cb5221 100644 --- a/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php +++ b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php @@ -14,152 +14,16 @@ class GetBudgetEntriesExportRequest extends Request public function resolveEndpoint(): string { - return "/budgets/{$this->budget}/entries-export"; + return "/budgets/{$this->budgetId}/entries-export"; } public function __construct( - protected string $budget, - protected ?string $filteruserId = null, - protected ?string $filteruserIdeq = null, - protected ?string $filteruserIdnq = null, - protected ?string $filteruserIdgt = null, - protected ?string $filteruserIdlt = null, - protected ?string $filteruserIdgte = null, - protected ?string $filteruserIdlte = null, - protected ?string $filteruserIdcontains = null, - protected ?string $filterbudgetId = null, - protected ?string $filterbudgetIdeq = null, - protected ?string $filterbudgetIdnq = null, - protected ?string $filterbudgetIdgt = null, - protected ?string $filterbudgetIdlt = null, - protected ?string $filterbudgetIdgte = null, - protected ?string $filterbudgetIdlte = null, - protected ?string $filterbudgetIdcontains = null, - protected ?string $filterstartedAt = null, - protected ?string $filterstartedAteq = null, - protected ?string $filterstartedAtnq = null, - protected ?string $filterstartedAtgt = null, - protected ?string $filterstartedAtlt = null, - protected ?string $filterstartedAtgte = null, - protected ?string $filterstartedAtlte = null, - protected ?string $filterstartedAtcontains = null, - protected ?string $filterendedAt = null, - protected ?string $filterendedAteq = null, - protected ?string $filterendedAtnq = null, - protected ?string $filterendedAtgt = null, - protected ?string $filterendedAtlt = null, - protected ?string $filterendedAtgte = null, - protected ?string $filterendedAtlte = null, - protected ?string $filterendedAtcontains = null, - protected ?string $filterhasOvertime = null, - protected ?string $filterhasOvertimeeq = null, - protected ?string $filterhasOvertimenq = null, - protected ?string $filterhasOvertimegt = null, - protected ?string $filterhasOvertimelt = null, - protected ?string $filterhasOvertimegte = null, - protected ?string $filterhasOvertimelte = null, - protected ?string $filterhasOvertimecontains = null, - protected ?string $filteruserFullName = null, - protected ?string $filteruserFullNameeq = null, - protected ?string $filteruserFullNamenq = null, - protected ?string $filteruserFullNamegt = null, - protected ?string $filteruserFullNamelt = null, - protected ?string $filteruserFullNamegte = null, - protected ?string $filteruserFullNamelte = null, - protected ?string $filteruserFullNamecontains = null, - protected ?string $filtercustomerId = null, - protected ?string $filtercustomerIdeq = null, - protected ?string $filtercustomerIdnq = null, - protected ?string $filtercustomerIdgt = null, - protected ?string $filtercustomerIdlt = null, - protected ?string $filtercustomerIdgte = null, - protected ?string $filtercustomerIdlte = null, - protected ?string $filtercustomerIdcontains = null, - protected ?string $filterticketNumber = null, - protected ?string $filterticketNumbereq = null, - protected ?string $filterticketNumbernq = null, - protected ?string $filterticketNumbergt = null, - protected ?string $filterticketNumberlt = null, - protected ?string $filterticketNumbergte = null, - protected ?string $filterticketNumberlte = null, - protected ?string $filterticketNumbercontains = null, - protected ?string $filtersettlement = null, - protected ?string $filterisInvoiced = null, - protected ?string $filterisInvoiceable = null, + protected string $budgetId, protected ?string $include = null, ) {} - public function defaultQuery(): array + protected function defaultQuery(): array { - return array_filter([ - 'filter[userId]' => $this->filteruserId, - 'filter[userId][eq]' => $this->filteruserIdeq, - 'filter[userId][nq]' => $this->filteruserIdnq, - 'filter[userId][gt]' => $this->filteruserIdgt, - 'filter[userId][lt]' => $this->filteruserIdlt, - 'filter[userId][gte]' => $this->filteruserIdgte, - 'filter[userId][lte]' => $this->filteruserIdlte, - 'filter[userId][contains]' => $this->filteruserIdcontains, - 'filter[budgetId]' => $this->filterbudgetId, - 'filter[budgetId][eq]' => $this->filterbudgetIdeq, - 'filter[budgetId][nq]' => $this->filterbudgetIdnq, - 'filter[budgetId][gt]' => $this->filterbudgetIdgt, - 'filter[budgetId][lt]' => $this->filterbudgetIdlt, - 'filter[budgetId][gte]' => $this->filterbudgetIdgte, - 'filter[budgetId][lte]' => $this->filterbudgetIdlte, - 'filter[budgetId][contains]' => $this->filterbudgetIdcontains, - 'filter[startedAt]' => $this->filterstartedAt, - 'filter[startedAt][eq]' => $this->filterstartedAteq, - 'filter[startedAt][nq]' => $this->filterstartedAtnq, - 'filter[startedAt][gt]' => $this->filterstartedAtgt, - 'filter[startedAt][lt]' => $this->filterstartedAtlt, - 'filter[startedAt][gte]' => $this->filterstartedAtgte, - 'filter[startedAt][lte]' => $this->filterstartedAtlte, - 'filter[startedAt][contains]' => $this->filterstartedAtcontains, - 'filter[endedAt]' => $this->filterendedAt, - 'filter[endedAt][eq]' => $this->filterendedAteq, - 'filter[endedAt][nq]' => $this->filterendedAtnq, - 'filter[endedAt][gt]' => $this->filterendedAtgt, - 'filter[endedAt][lt]' => $this->filterendedAtlt, - 'filter[endedAt][gte]' => $this->filterendedAtgte, - 'filter[endedAt][lte]' => $this->filterendedAtlte, - 'filter[endedAt][contains]' => $this->filterendedAtcontains, - 'filter[hasOvertime]' => $this->filterhasOvertime, - 'filter[hasOvertime][eq]' => $this->filterhasOvertimeeq, - 'filter[hasOvertime][nq]' => $this->filterhasOvertimenq, - 'filter[hasOvertime][gt]' => $this->filterhasOvertimegt, - 'filter[hasOvertime][lt]' => $this->filterhasOvertimelt, - 'filter[hasOvertime][gte]' => $this->filterhasOvertimegte, - 'filter[hasOvertime][lte]' => $this->filterhasOvertimelte, - 'filter[hasOvertime][contains]' => $this->filterhasOvertimecontains, - 'filter[userFullName]' => $this->filteruserFullName, - 'filter[userFullName][eq]' => $this->filteruserFullNameeq, - 'filter[userFullName][nq]' => $this->filteruserFullNamenq, - 'filter[userFullName][gt]' => $this->filteruserFullNamegt, - 'filter[userFullName][lt]' => $this->filteruserFullNamelt, - 'filter[userFullName][gte]' => $this->filteruserFullNamegte, - 'filter[userFullName][lte]' => $this->filteruserFullNamelte, - 'filter[userFullName][contains]' => $this->filteruserFullNamecontains, - 'filter[customerId]' => $this->filtercustomerId, - 'filter[customerId][eq]' => $this->filtercustomerIdeq, - 'filter[customerId][nq]' => $this->filtercustomerIdnq, - 'filter[customerId][gt]' => $this->filtercustomerIdgt, - 'filter[customerId][lt]' => $this->filtercustomerIdlt, - 'filter[customerId][gte]' => $this->filtercustomerIdgte, - 'filter[customerId][lte]' => $this->filtercustomerIdlte, - 'filter[customerId][contains]' => $this->filtercustomerIdcontains, - 'filter[ticketNumber]' => $this->filterticketNumber, - 'filter[ticketNumber][eq]' => $this->filterticketNumbereq, - 'filter[ticketNumber][nq]' => $this->filterticketNumbernq, - 'filter[ticketNumber][gt]' => $this->filterticketNumbergt, - 'filter[ticketNumber][lt]' => $this->filterticketNumberlt, - 'filter[ticketNumber][gte]' => $this->filterticketNumbergte, - 'filter[ticketNumber][lte]' => $this->filterticketNumberlte, - 'filter[ticketNumber][contains]' => $this->filterticketNumbercontains, - 'filter[settlement]' => $this->filtersettlement, - 'filter[isInvoiced]' => $this->filterisInvoiced, - 'filter[isInvoiceable]' => $this->filterisInvoiceable, - 'include' => $this->include, - ]); + return array_filter(['include' => $this->include]); } } diff --git a/src/Requests/Entry/DeleteEntryRequest.php b/src/Requests/Entry/DeleteEntryRequest.php index 5ef24c9..96491dd 100644 --- a/src/Requests/Entry/DeleteEntryRequest.php +++ b/src/Requests/Entry/DeleteEntryRequest.php @@ -14,10 +14,10 @@ class DeleteEntryRequest extends Request public function resolveEndpoint(): string { - return "/entries/{$this->entry}"; + return "/entries/{$this->entryId}"; } public function __construct( - protected string $entry, + protected string $entryId, ) {} } diff --git a/src/Requests/Entry/GetEntriesRequest.php b/src/Requests/Entry/GetEntriesRequest.php index ad65409..d654b7b 100644 --- a/src/Requests/Entry/GetEntriesRequest.php +++ b/src/Requests/Entry/GetEntriesRequest.php @@ -25,7 +25,7 @@ public function __construct( protected ?string $include = null, ) {} - public function defaultQuery(): array + protected function defaultQuery(): array { return array_filter(['include' => $this->include]); } diff --git a/src/Requests/Entry/GetEntryRequest.php b/src/Requests/Entry/GetEntryRequest.php index 12af9c7..7d6924a 100644 --- a/src/Requests/Entry/GetEntryRequest.php +++ b/src/Requests/Entry/GetEntryRequest.php @@ -14,10 +14,10 @@ class GetEntryRequest extends Request public function resolveEndpoint(): string { - return "/entries/{$this->entry}"; + return "/entries/{$this->entryId}"; } public function __construct( - protected string $entry, + protected string $entryId, ) {} } diff --git a/src/Requests/Entry/PatchEntryRequest.php b/src/Requests/Entry/PatchEntryRequest.php index 75ce82f..9512f3b 100644 --- a/src/Requests/Entry/PatchEntryRequest.php +++ b/src/Requests/Entry/PatchEntryRequest.php @@ -19,12 +19,15 @@ class PatchEntryRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/entries/{$this->entry}"; + return "/entries/{$this->entryId}"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $entry, - protected Model|array|null $data, + protected string $entryId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Entry/PostEntriesRequest.php b/src/Requests/Entry/PostEntriesRequest.php index b0720dd..3fb2e9c 100644 --- a/src/Requests/Entry/PostEntriesRequest.php +++ b/src/Requests/Entry/PostEntriesRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/entries'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php b/src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php index 94501ca..96e86e5 100644 --- a/src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php +++ b/src/Requests/EntrySuggestion/DeleteEntrySuggestionRequest.php @@ -14,10 +14,10 @@ class DeleteEntrySuggestionRequest extends Request public function resolveEndpoint(): string { - return "/entry-suggestions/{$this->entrySuggestion}"; + return "/entry-suggestions/{$this->entrySuggestionId}"; } public function __construct( - protected string $entrySuggestion, + protected string $entrySuggestionId, ) {} } diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php b/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php index d127024..d48fcc2 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php @@ -14,10 +14,10 @@ class GetEntrySuggestionRequest extends Request public function resolveEndpoint(): string { - return "/entry-suggestions/{$this->entrySuggestion}"; + return "/entry-suggestions/{$this->entrySuggestionId}"; } public function __construct( - protected string $entrySuggestion, + protected string $entrySuggestionId, ) {} } diff --git a/src/Requests/Event/PostEventsRequest.php b/src/Requests/Event/PostEventsRequest.php index a992e98..a956f33 100644 --- a/src/Requests/Event/PostEventsRequest.php +++ b/src/Requests/Event/PostEventsRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/events'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Incident/GetIncidentRequest.php b/src/Requests/Incident/GetIncidentRequest.php index 92e0be3..41c0265 100644 --- a/src/Requests/Incident/GetIncidentRequest.php +++ b/src/Requests/Incident/GetIncidentRequest.php @@ -14,10 +14,10 @@ class GetIncidentRequest extends Request public function resolveEndpoint(): string { - return "/incidents/{$this->incident}"; + return "/incidents/{$this->incidentId}"; } public function __construct( - protected string $incident, + protected string $incidentId, ) {} } diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php index 6f829e5..02dee50 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php @@ -19,12 +19,15 @@ class PostOvertimeMarkAsExportedRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/overtimes/{$this->overtime}/mark-as-exported"; + return "/overtimes/{$this->overtimeId}/mark-as-exported"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $overtime, - protected Model|array|null $data, + protected string $overtimeId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php index 21efb87..78a262a 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php @@ -19,12 +19,15 @@ class PostEntryMarkAsInvoicedRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/entries/{$this->entry}/mark-as-invoiced"; + return "/entries/{$this->entryId}/mark-as-invoiced"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $entry, - protected Model|array|null $data, + protected string $entryId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Number/GetIncidentsNumberRequest.php b/src/Requests/Number/GetIncidentsNumberRequest.php index e96a58a..911051b 100644 --- a/src/Requests/Number/GetIncidentsNumberRequest.php +++ b/src/Requests/Number/GetIncidentsNumberRequest.php @@ -14,10 +14,10 @@ class GetIncidentsNumberRequest extends Request public function resolveEndpoint(): string { - return "/incidents/number/{$this->incident}"; + return "/incidents/number/{$this->incidentId}"; } public function __construct( - protected string $incident, + protected string $incidentId, ) {} } diff --git a/src/Requests/Period/GetBudgetPeriodsRequest.php b/src/Requests/Period/GetBudgetPeriodsRequest.php index 960b490..ddfa825 100644 --- a/src/Requests/Period/GetBudgetPeriodsRequest.php +++ b/src/Requests/Period/GetBudgetPeriodsRequest.php @@ -14,10 +14,10 @@ class GetBudgetPeriodsRequest extends Request public function resolveEndpoint(): string { - return "/budgets/{$this->budget}/periods"; + return "/budgets/{$this->budgetId}/periods"; } public function __construct( - protected string $budget, + protected string $budgetId, ) {} } diff --git a/src/Requests/Team/DeleteTeamRequest.php b/src/Requests/Team/DeleteTeamRequest.php index 8a48117..484a10d 100644 --- a/src/Requests/Team/DeleteTeamRequest.php +++ b/src/Requests/Team/DeleteTeamRequest.php @@ -14,10 +14,10 @@ class DeleteTeamRequest extends Request public function resolveEndpoint(): string { - return "/teams/{$this->team}"; + return "/teams/{$this->teamId}"; } public function __construct( - protected string $team, + protected string $teamId, ) {} } diff --git a/src/Requests/Team/GetTeamRequest.php b/src/Requests/Team/GetTeamRequest.php index 29ecef0..9ab0902 100644 --- a/src/Requests/Team/GetTeamRequest.php +++ b/src/Requests/Team/GetTeamRequest.php @@ -14,10 +14,10 @@ class GetTeamRequest extends Request public function resolveEndpoint(): string { - return "/teams/{$this->team}"; + return "/teams/{$this->teamId}"; } public function __construct( - protected string $team, + protected string $teamId, ) {} } diff --git a/src/Requests/Team/PatchTeamRequest.php b/src/Requests/Team/PatchTeamRequest.php index c58bbeb..041e999 100644 --- a/src/Requests/Team/PatchTeamRequest.php +++ b/src/Requests/Team/PatchTeamRequest.php @@ -19,12 +19,15 @@ class PatchTeamRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/teams/{$this->team}"; + return "/teams/{$this->teamId}"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $team, - protected Model|array|null $data, + protected string $teamId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/Team/PostTeamsRequest.php b/src/Requests/Team/PostTeamsRequest.php index 22b663f..86db651 100644 --- a/src/Requests/Team/PostTeamsRequest.php +++ b/src/Requests/Team/PostTeamsRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/teams'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/User/DeleteUserRequest.php b/src/Requests/User/DeleteUserRequest.php index 1e47686..4edadca 100644 --- a/src/Requests/User/DeleteUserRequest.php +++ b/src/Requests/User/DeleteUserRequest.php @@ -14,10 +14,10 @@ class DeleteUserRequest extends Request public function resolveEndpoint(): string { - return "/users/{$this->user}"; + return "/users/{$this->userId}"; } public function __construct( - protected string $user, + protected string $userId, ) {} } diff --git a/src/Requests/User/GetUserRequest.php b/src/Requests/User/GetUserRequest.php index e1ceeb7..34b11a4 100644 --- a/src/Requests/User/GetUserRequest.php +++ b/src/Requests/User/GetUserRequest.php @@ -14,10 +14,10 @@ class GetUserRequest extends Request public function resolveEndpoint(): string { - return "/users/{$this->user}"; + return "/users/{$this->userId}"; } public function __construct( - protected string $user, + protected string $userId, ) {} } diff --git a/src/Requests/User/PatchUserRequest.php b/src/Requests/User/PatchUserRequest.php index 29f1a2e..96975b2 100644 --- a/src/Requests/User/PatchUserRequest.php +++ b/src/Requests/User/PatchUserRequest.php @@ -19,12 +19,15 @@ class PatchUserRequest extends Request implements HasBody public function resolveEndpoint(): string { - return "/users/{$this->user}"; + return "/users/{$this->userId}"; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected string $user, - protected Model|array|null $data, + protected string $userId, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Requests/User/PostUsersRequest.php b/src/Requests/User/PostUsersRequest.php index 4efbafe..3793b47 100644 --- a/src/Requests/User/PostUsersRequest.php +++ b/src/Requests/User/PostUsersRequest.php @@ -22,8 +22,11 @@ public function resolveEndpoint(): string return '/users'; } + /** + * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + */ public function __construct( - protected Model|array|null $data, + protected Model|array|null $data = null, ) {} protected function defaultBody(): array diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php index b260341..3d871a0 100644 --- a/src/Resource/Approve.php +++ b/src/Resource/Approve.php @@ -4,14 +4,16 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Approve\PostOvertimeApproveRequest; class Approve extends BaseResource { - public function postOvertimeApprove( - string $overtime, - \Timatic\SDK\Foundation\Model|array|null $data = null, - ): Response { - return $this->connector->send(new PostOvertimeApproveRequest($overtime, $data)); + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postOvertimeApprove(string $overtimeId, Model|array|null $data = null): Response + { + return $this->connector->send(new PostOvertimeApproveRequest($overtimeId, $data)); } } diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index 5976c1d..33ae734 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -4,6 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Budget\DeleteBudgetRequest; use Timatic\SDK\Requests\Budget\GetBudgetRequest; use Timatic\SDK\Requests\Budget\GetBudgetsRequest; @@ -37,23 +38,29 @@ public function getBudgets( return $this->connector->send(new GetBudgetsRequest($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); } - public function postBudgets(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postBudgets(Model|array|null $data = null): Response { return $this->connector->send(new PostBudgetsRequest($data)); } - public function getBudget(string $budget): Response + public function getBudget(string $budgetId): Response { - return $this->connector->send(new GetBudgetRequest($budget)); + return $this->connector->send(new GetBudgetRequest($budgetId)); } - public function deleteBudget(string $budget): Response + public function deleteBudget(string $budgetId): Response { - return $this->connector->send(new DeleteBudgetRequest($budget)); + return $this->connector->send(new DeleteBudgetRequest($budgetId)); } - public function patchBudget(string $budget, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function patchBudget(string $budgetId, Model|array|null $data = null): Response { - return $this->connector->send(new PatchBudgetRequest($budget, $data)); + return $this->connector->send(new PatchBudgetRequest($budgetId, $data)); } } diff --git a/src/Resource/Change.php b/src/Resource/Change.php index 2a06fdc..c824f8e 100644 --- a/src/Resource/Change.php +++ b/src/Resource/Change.php @@ -9,9 +9,9 @@ class Change extends BaseResource { - public function getChange(string $change): Response + public function getChange(string $changeId): Response { - return $this->connector->send(new GetChangeRequest($change)); + return $this->connector->send(new GetChangeRequest($changeId)); } public function getChanges(): Response diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index e2c03ac..232f47a 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -4,18 +4,25 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Correction\PatchCorrectionRequest; use Timatic\SDK\Requests\Correction\PostCorrectionsRequest; class Correction extends BaseResource { - public function postCorrections(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postCorrections(Model|array|null $data = null): Response { return $this->connector->send(new PostCorrectionsRequest($data)); } - public function patchCorrection(string $correction, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function patchCorrection(string $correctionId, Model|array|null $data = null): Response { - return $this->connector->send(new PatchCorrectionRequest($correction, $data)); + return $this->connector->send(new PatchCorrectionRequest($correctionId, $data)); } } diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index 4aaedae..a786530 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -4,6 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Customer\DeleteCustomerRequest; use Timatic\SDK\Requests\Customer\GetCustomerRequest; use Timatic\SDK\Requests\Customer\GetCustomersRequest; @@ -17,23 +18,29 @@ public function getCustomers(?string $filterexternalId = null, ?string $filterex return $this->connector->send(new GetCustomersRequest($filterexternalId, $filterexternalIdeq)); } - public function postCustomers(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postCustomers(Model|array|null $data = null): Response { return $this->connector->send(new PostCustomersRequest($data)); } - public function getCustomer(string $customer): Response + public function getCustomer(string $customerId): Response { - return $this->connector->send(new GetCustomerRequest($customer)); + return $this->connector->send(new GetCustomerRequest($customerId)); } - public function deleteCustomer(string $customer): Response + public function deleteCustomer(string $customerId): Response { - return $this->connector->send(new DeleteCustomerRequest($customer)); + return $this->connector->send(new DeleteCustomerRequest($customerId)); } - public function patchCustomer(string $customer, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function patchCustomer(string $customerId, Model|array|null $data = null): Response { - return $this->connector->send(new PatchCustomerRequest($customer, $data)); + return $this->connector->send(new PatchCustomerRequest($customerId, $data)); } } diff --git a/src/Resource/EntriesExport.php b/src/Resource/EntriesExport.php index 4dea7a5..637e3e7 100644 --- a/src/Resource/EntriesExport.php +++ b/src/Resource/EntriesExport.php @@ -9,7 +9,7 @@ class EntriesExport extends BaseResource { public function getBudgetEntriesExport( - string $budget, + string $budgetId, ?string $filteruserId = null, ?string $filteruserIdeq = null, ?string $filteruserIdnq = null, @@ -79,6 +79,6 @@ public function getBudgetEntriesExport( ?string $filterisInvoiceable = null, ?string $include = null, ): Response { - return $this->connector->send(new GetBudgetEntriesExportRequest($budget, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); + return $this->connector->send(new GetBudgetEntriesExportRequest($budgetId, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); } } diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 61147ff..1145fd0 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -4,6 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Entry\DeleteEntryRequest; use Timatic\SDK\Requests\Entry\GetEntriesRequest; use Timatic\SDK\Requests\Entry\GetEntryRequest; @@ -85,23 +86,29 @@ public function getEntries( return $this->connector->send(new GetEntriesRequest($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); } - public function postEntries(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postEntries(Model|array|null $data = null): Response { return $this->connector->send(new PostEntriesRequest($data)); } - public function getEntry(string $entry): Response + public function getEntry(string $entryId): Response { - return $this->connector->send(new GetEntryRequest($entry)); + return $this->connector->send(new GetEntryRequest($entryId)); } - public function deleteEntry(string $entry): Response + public function deleteEntry(string $entryId): Response { - return $this->connector->send(new DeleteEntryRequest($entry)); + return $this->connector->send(new DeleteEntryRequest($entryId)); } - public function patchEntry(string $entry, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function patchEntry(string $entryId, Model|array|null $data = null): Response { - return $this->connector->send(new PatchEntryRequest($entry, $data)); + return $this->connector->send(new PatchEntryRequest($entryId, $data)); } } diff --git a/src/Resource/EntrySuggestion.php b/src/Resource/EntrySuggestion.php index 4dd60af..2ce5833 100644 --- a/src/Resource/EntrySuggestion.php +++ b/src/Resource/EntrySuggestion.php @@ -23,13 +23,13 @@ public function getEntrySuggestions( return $this->connector->send(new GetEntrySuggestionsRequest($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); } - public function getEntrySuggestion(string $entrySuggestion): Response + public function getEntrySuggestion(string $entrySuggestionId): Response { - return $this->connector->send(new GetEntrySuggestionRequest($entrySuggestion)); + return $this->connector->send(new GetEntrySuggestionRequest($entrySuggestionId)); } - public function deleteEntrySuggestion(string $entrySuggestion): Response + public function deleteEntrySuggestion(string $entrySuggestionId): Response { - return $this->connector->send(new DeleteEntrySuggestionRequest($entrySuggestion)); + return $this->connector->send(new DeleteEntrySuggestionRequest($entrySuggestionId)); } } diff --git a/src/Resource/Event.php b/src/Resource/Event.php index 5b46e7d..7842730 100644 --- a/src/Resource/Event.php +++ b/src/Resource/Event.php @@ -4,11 +4,15 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Event\PostEventsRequest; class Event extends BaseResource { - public function postEvents(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postEvents(Model|array|null $data = null): Response { return $this->connector->send(new PostEventsRequest($data)); } diff --git a/src/Resource/Incident.php b/src/Resource/Incident.php index 0c4b8b2..71598fc 100644 --- a/src/Resource/Incident.php +++ b/src/Resource/Incident.php @@ -9,9 +9,9 @@ class Incident extends BaseResource { - public function getIncident(string $incident): Response + public function getIncident(string $incidentId): Response { - return $this->connector->send(new GetIncidentRequest($incident)); + return $this->connector->send(new GetIncidentRequest($incidentId)); } public function getIncidents(): Response diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php index 810e2b7..15cc7c1 100644 --- a/src/Resource/MarkAsExported.php +++ b/src/Resource/MarkAsExported.php @@ -4,14 +4,16 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\MarkAsExported\PostOvertimeMarkAsExportedRequest; class MarkAsExported extends BaseResource { - public function postOvertimeMarkAsExported( - string $overtime, - \Timatic\SDK\Foundation\Model|array|null $data = null, - ): Response { - return $this->connector->send(new PostOvertimeMarkAsExportedRequest($overtime, $data)); + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postOvertimeMarkAsExported(string $overtimeId, Model|array|null $data = null): Response + { + return $this->connector->send(new PostOvertimeMarkAsExportedRequest($overtimeId, $data)); } } diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php index 3338375..8e74fba 100644 --- a/src/Resource/MarkAsInvoiced.php +++ b/src/Resource/MarkAsInvoiced.php @@ -4,14 +4,16 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\MarkAsInvoiced\PostEntryMarkAsInvoicedRequest; class MarkAsInvoiced extends BaseResource { - public function postEntryMarkAsInvoiced( - string $entry, - \Timatic\SDK\Foundation\Model|array|null $data = null, - ): Response { - return $this->connector->send(new PostEntryMarkAsInvoicedRequest($entry, $data)); + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postEntryMarkAsInvoiced(string $entryId, Model|array|null $data = null): Response + { + return $this->connector->send(new PostEntryMarkAsInvoicedRequest($entryId, $data)); } } diff --git a/src/Resource/Number.php b/src/Resource/Number.php index d658e64..77c906e 100644 --- a/src/Resource/Number.php +++ b/src/Resource/Number.php @@ -8,8 +8,8 @@ class Number extends BaseResource { - public function getIncidentsNumber(string $incident): Response + public function getIncidentsNumber(string $incidentId): Response { - return $this->connector->send(new GetIncidentsNumberRequest($incident)); + return $this->connector->send(new GetIncidentsNumberRequest($incidentId)); } } diff --git a/src/Resource/Period.php b/src/Resource/Period.php index e35fb6b..56d8c3e 100644 --- a/src/Resource/Period.php +++ b/src/Resource/Period.php @@ -8,8 +8,8 @@ class Period extends BaseResource { - public function getBudgetPeriods(string $budget): Response + public function getBudgetPeriods(string $budgetId): Response { - return $this->connector->send(new GetBudgetPeriodsRequest($budget)); + return $this->connector->send(new GetBudgetPeriodsRequest($budgetId)); } } diff --git a/src/Resource/Team.php b/src/Resource/Team.php index 97854e7..3838a4f 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -4,6 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\Team\DeleteTeamRequest; use Timatic\SDK\Requests\Team\GetTeamRequest; use Timatic\SDK\Requests\Team\GetTeamsRequest; @@ -17,23 +18,29 @@ public function getTeams(): Response return $this->connector->send(new GetTeamsRequest); } - public function postTeams(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postTeams(Model|array|null $data = null): Response { return $this->connector->send(new PostTeamsRequest($data)); } - public function getTeam(string $team): Response + public function getTeam(string $teamId): Response { - return $this->connector->send(new GetTeamRequest($team)); + return $this->connector->send(new GetTeamRequest($teamId)); } - public function deleteTeam(string $team): Response + public function deleteTeam(string $teamId): Response { - return $this->connector->send(new DeleteTeamRequest($team)); + return $this->connector->send(new DeleteTeamRequest($teamId)); } - public function patchTeam(string $team, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function patchTeam(string $teamId, Model|array|null $data = null): Response { - return $this->connector->send(new PatchTeamRequest($team, $data)); + return $this->connector->send(new PatchTeamRequest($teamId, $data)); } } diff --git a/src/Resource/User.php b/src/Resource/User.php index 84e0799..ce83640 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -4,6 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; +use Timatic\SDK\Foundation\Model; use Timatic\SDK\Requests\User\DeleteUserRequest; use Timatic\SDK\Requests\User\GetUserRequest; use Timatic\SDK\Requests\User\GetUsersRequest; @@ -17,23 +18,29 @@ public function getUsers(?string $filterexternalId = null, ?string $filterextern return $this->connector->send(new GetUsersRequest($filterexternalId, $filterexternalIdeq)); } - public function postUsers(\Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function postUsers(Model|array|null $data = null): Response { return $this->connector->send(new PostUsersRequest($data)); } - public function getUser(string $user): Response + public function getUser(string $userId): Response { - return $this->connector->send(new GetUserRequest($user)); + return $this->connector->send(new GetUserRequest($userId)); } - public function deleteUser(string $user): Response + public function deleteUser(string $userId): Response { - return $this->connector->send(new DeleteUserRequest($user)); + return $this->connector->send(new DeleteUserRequest($userId)); } - public function patchUser(string $user, \Timatic\SDK\Foundation\Model|array|null $data = null): Response + /** + * @param Timatic\SDK\Foundation\Model|array|null $data Request data + */ + public function patchUser(string $userId, Model|array|null $data = null): Response { - return $this->connector->send(new PatchUserRequest($user, $data)); + return $this->connector->send(new PatchUserRequest($userId, $data)); } } diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index 24b7551..d8ff63f 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -14,7 +14,7 @@ ]); $response = $this->timaticConnector->approve()->postOvertimeApprove( - overtime: 'test string' + overtimeId: 'test string' ); Saloon::assertSent(PostOvertimeApproveRequest::class); diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 63aa75d..d31944b 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -65,7 +65,7 @@ ]); $response = $this->timaticConnector->budget()->getBudget( - budget: 'test string' + budgetId: 'test string' ); Saloon::assertSent(GetBudgetRequest::class); @@ -79,7 +79,7 @@ ]); $response = $this->timaticConnector->budget()->deleteBudget( - budget: 'test string' + budgetId: 'test string' ); Saloon::assertSent(DeleteBudgetRequest::class); @@ -93,7 +93,7 @@ ]); $response = $this->timaticConnector->budget()->patchBudget( - budget: 'test string' + budgetId: 'test string' ); Saloon::assertSent(PatchBudgetRequest::class); diff --git a/tests/Requests/ChangeTest.php b/tests/Requests/ChangeTest.php index 5ea55a3..c903f7c 100644 --- a/tests/Requests/ChangeTest.php +++ b/tests/Requests/ChangeTest.php @@ -15,7 +15,7 @@ ]); $response = $this->timaticConnector->change()->getChange( - change: 'test string' + changeId: 'test string' ); Saloon::assertSent(GetChangeRequest::class); diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index e439699..f5b4319 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -29,7 +29,7 @@ ]); $response = $this->timaticConnector->correction()->patchCorrection( - correction: 'test string' + correctionId: 'test string' ); Saloon::assertSent(PatchCorrectionRequest::class); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index c6c1255..d3d1a88 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -47,7 +47,7 @@ ]); $response = $this->timaticConnector->customer()->getCustomer( - customer: 'test string' + customerId: 'test string' ); Saloon::assertSent(GetCustomerRequest::class); @@ -61,7 +61,7 @@ ]); $response = $this->timaticConnector->customer()->deleteCustomer( - customer: 'test string' + customerId: 'test string' ); Saloon::assertSent(DeleteCustomerRequest::class); @@ -75,7 +75,7 @@ ]); $response = $this->timaticConnector->customer()->patchCustomer( - customer: 'test string' + customerId: 'test string' ); Saloon::assertSent(PatchCustomerRequest::class); diff --git a/tests/Requests/EntriesExportTest.php b/tests/Requests/EntriesExportTest.php index ba6e49e..29b5aac 100644 --- a/tests/Requests/EntriesExportTest.php +++ b/tests/Requests/EntriesExportTest.php @@ -14,7 +14,7 @@ ]); $response = $this->timaticConnector->entriesExport()->getBudgetEntriesExport( - budget: 'test string', + budgetId: 'test string', filteruserId: 'test string', filteruserIdeq: 'test string', filteruserIdnq: 'test string', diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index e67f587..62cbe24 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -37,7 +37,7 @@ ]); $response = $this->timaticConnector->entrySuggestion()->getEntrySuggestion( - entrySuggestion: 'test string' + entrySuggestionId: 'test string' ); Saloon::assertSent(GetEntrySuggestionRequest::class); @@ -51,7 +51,7 @@ ]); $response = $this->timaticConnector->entrySuggestion()->deleteEntrySuggestion( - entrySuggestion: 'test string' + entrySuggestionId: 'test string' ); Saloon::assertSent(DeleteEntrySuggestionRequest::class); diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 40ce354..6cdbef7 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -113,7 +113,7 @@ ]); $response = $this->timaticConnector->entry()->getEntry( - entry: 'test string' + entryId: 'test string' ); Saloon::assertSent(GetEntryRequest::class); @@ -127,7 +127,7 @@ ]); $response = $this->timaticConnector->entry()->deleteEntry( - entry: 'test string' + entryId: 'test string' ); Saloon::assertSent(DeleteEntryRequest::class); @@ -141,7 +141,7 @@ ]); $response = $this->timaticConnector->entry()->patchEntry( - entry: 'test string' + entryId: 'test string' ); Saloon::assertSent(PatchEntryRequest::class); diff --git a/tests/Requests/IncidentTest.php b/tests/Requests/IncidentTest.php index 554a688..936818f 100644 --- a/tests/Requests/IncidentTest.php +++ b/tests/Requests/IncidentTest.php @@ -15,7 +15,7 @@ ]); $response = $this->timaticConnector->incident()->getIncident( - incident: 'test string' + incidentId: 'test string' ); Saloon::assertSent(GetIncidentRequest::class); diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index 3afa3b4..888d92c 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -14,7 +14,7 @@ ]); $response = $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported( - overtime: 'test string' + overtimeId: 'test string' ); Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index 4aec5fc..b930999 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -14,7 +14,7 @@ ]); $response = $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced( - entry: 'test string' + entryId: 'test string' ); Saloon::assertSent(PostEntryMarkAsInvoicedRequest::class); diff --git a/tests/Requests/NumberTest.php b/tests/Requests/NumberTest.php index ab791b3..1d2dab5 100644 --- a/tests/Requests/NumberTest.php +++ b/tests/Requests/NumberTest.php @@ -14,7 +14,7 @@ ]); $response = $this->timaticConnector->number()->getIncidentsNumber( - incident: 'test string' + incidentId: 'test string' ); Saloon::assertSent(GetIncidentsNumberRequest::class); diff --git a/tests/Requests/PeriodTest.php b/tests/Requests/PeriodTest.php index 947c3e4..9643da6 100644 --- a/tests/Requests/PeriodTest.php +++ b/tests/Requests/PeriodTest.php @@ -14,7 +14,7 @@ ]); $response = $this->timaticConnector->period()->getBudgetPeriods( - budget: 'test string' + budgetId: 'test string' ); Saloon::assertSent(GetBudgetPeriodsRequest::class); diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index 0a7bc8f..776ae94 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -46,7 +46,7 @@ ]); $response = $this->timaticConnector->team()->getTeam( - team: 'test string' + teamId: 'test string' ); Saloon::assertSent(GetTeamRequest::class); @@ -60,7 +60,7 @@ ]); $response = $this->timaticConnector->team()->deleteTeam( - team: 'test string' + teamId: 'test string' ); Saloon::assertSent(DeleteTeamRequest::class); @@ -74,7 +74,7 @@ ]); $response = $this->timaticConnector->team()->patchTeam( - team: 'test string' + teamId: 'test string' ); Saloon::assertSent(PatchTeamRequest::class); diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index 995f978..e426110 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -47,7 +47,7 @@ ]); $response = $this->timaticConnector->user()->getUser( - user: 'test string' + userId: 'test string' ); Saloon::assertSent(GetUserRequest::class); @@ -61,7 +61,7 @@ ]); $response = $this->timaticConnector->user()->deleteUser( - user: 'test string' + userId: 'test string' ); Saloon::assertSent(DeleteUserRequest::class); @@ -75,7 +75,7 @@ ]); $response = $this->timaticConnector->user()->patchUser( - user: 'test string' + userId: 'test string' ); Saloon::assertSent(PatchUserRequest::class); From 287323d469ca73b70f8996317de9cb10ed71340d Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 13:33:48 +0100 Subject: [PATCH 25/62] merge concerns and foundation --- CLAUDE.md | 2 +- generator/JsonApiDtoGenerator.php | 2 +- generator/JsonApiRequestGenerator.php | 4 ++-- generator/JsonApiResourceGenerator.php | 4 ++-- src/{Foundation => Concerns}/HasAttributes.php | 2 +- src/{Foundation => Concerns}/Model.php | 2 +- src/{Foundation => Concerns}/ModelInterface.php | 2 +- src/Dto/Approve.php | 2 +- src/Dto/Budget.php | 2 +- src/Dto/BudgetTimeSpentTotal.php | 2 +- src/Dto/BudgetType.php | 2 +- src/Dto/Correction.php | 2 +- src/Dto/Customer.php | 2 +- src/Dto/DailyProgress.php | 2 +- src/Dto/Entry.php | 2 +- src/Dto/EntrySuggestion.php | 2 +- src/Dto/Event.php | 2 +- src/Dto/ExportMail.php | 2 +- src/Dto/MarkAsExported.php | 2 +- src/Dto/MarkAsInvoiced.php | 2 +- src/Dto/Overtime.php | 2 +- src/Dto/Team.php | 2 +- src/Dto/TimeSpentTotal.php | 2 +- src/Dto/User.php | 2 +- src/Dto/UserCustomerHoursAggregate.php | 2 +- src/Requests/Approve/PostOvertimeApproveRequest.php | 4 ++-- src/Requests/Budget/PatchBudgetRequest.php | 4 ++-- src/Requests/Budget/PostBudgetsRequest.php | 4 ++-- src/Requests/Correction/PatchCorrectionRequest.php | 4 ++-- src/Requests/Correction/PostCorrectionsRequest.php | 4 ++-- src/Requests/Customer/PatchCustomerRequest.php | 4 ++-- src/Requests/Customer/PostCustomersRequest.php | 4 ++-- src/Requests/Entry/PatchEntryRequest.php | 4 ++-- src/Requests/Entry/PostEntriesRequest.php | 4 ++-- src/Requests/Event/PostEventsRequest.php | 4 ++-- .../MarkAsExported/PostOvertimeMarkAsExportedRequest.php | 4 ++-- .../MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php | 4 ++-- src/Requests/Team/PatchTeamRequest.php | 4 ++-- src/Requests/Team/PostTeamsRequest.php | 4 ++-- src/Requests/User/PatchUserRequest.php | 4 ++-- src/Requests/User/PostUsersRequest.php | 4 ++-- src/Resource/Approve.php | 4 ++-- src/Resource/Budget.php | 6 +++--- src/Resource/Correction.php | 6 +++--- src/Resource/Customer.php | 6 +++--- src/Resource/Entry.php | 6 +++--- src/Resource/Event.php | 4 ++-- src/Resource/MarkAsExported.php | 4 ++-- src/Resource/MarkAsInvoiced.php | 4 ++-- src/Resource/Team.php | 6 +++--- src/Resource/User.php | 6 +++--- 51 files changed, 85 insertions(+), 85 deletions(-) rename src/{Foundation => Concerns}/HasAttributes.php (97%) rename src/{Foundation => Concerns}/Model.php (98%) rename src/{Foundation => Concerns}/ModelInterface.php (85%) diff --git a/CLAUDE.md b/CLAUDE.md index d576430..4233068 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -280,7 +280,7 @@ PUT is intentionally excluded as PATCH provides better semantics for partial upd - **`tests/*Test.php`** - All test files (auto-generated from OpenAPI endpoints, Pest.php and TestCase.php are preserved) ### āœ… SAFE TO EDIT: -- **`src/Foundation/`** - Base classes (Model, HasAttributes, etc.) +- **`src/Concerns/`** - Base classes and traits (Model, HasAttributes, HasFilters, etc.) - **`src/Attributes/`** - PHP attributes (Property, DateTime) - **`src/Responses/`** - Custom response classes (TimaticResponse) - **`src/Pagination/`** - Pagination classes (JsonApiPaginator) diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php index eda5631..8dcd145 100644 --- a/generator/JsonApiDtoGenerator.php +++ b/generator/JsonApiDtoGenerator.php @@ -15,7 +15,7 @@ use Nette\PhpGenerator\PhpFile; use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class JsonApiDtoGenerator extends Generator { diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 15c922b..7229ae5 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -11,7 +11,7 @@ use Nette\PhpGenerator\ClassType; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class JsonApiRequestGenerator extends RequestGenerator { @@ -81,7 +81,7 @@ protected function customizeConstructor($classConstructor, ClassType $classType, $namespace->addUse(Model::class); $dataParam = new Parameter( - type: 'Timatic\\SDK\\Foundation\\Model|array|null', + type: 'Timatic\\SDK\\Concerns\\Model|array|null', nullable: true, name: 'data', description: 'Request data', diff --git a/generator/JsonApiResourceGenerator.php b/generator/JsonApiResourceGenerator.php index ca72ff3..e2f34fd 100644 --- a/generator/JsonApiResourceGenerator.php +++ b/generator/JsonApiResourceGenerator.php @@ -7,7 +7,7 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\ResourceGenerator; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class JsonApiResourceGenerator extends ResourceGenerator { @@ -70,7 +70,7 @@ protected function customizeResourceMethod(\Nette\PhpGenerator\Method $method, $ $namespace->addUse(Model::class); $dataParam = new Parameter( - type: 'Timatic\\SDK\\Foundation\\Model|array|null', + type: 'Timatic\\SDK\\Concerns\\Model|array|null', nullable: true, name: 'data', description: 'Request data', diff --git a/src/Foundation/HasAttributes.php b/src/Concerns/HasAttributes.php similarity index 97% rename from src/Foundation/HasAttributes.php rename to src/Concerns/HasAttributes.php index 72c7cd4..468c98b 100644 --- a/src/Foundation/HasAttributes.php +++ b/src/Concerns/HasAttributes.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Foundation; +namespace Timatic\SDK\Concerns; use ReflectionClass; use Timatic\SDK\Attributes\Property; diff --git a/src/Foundation/Model.php b/src/Concerns/Model.php similarity index 98% rename from src/Foundation/Model.php rename to src/Concerns/Model.php index 9722181..0bf8815 100644 --- a/src/Foundation/Model.php +++ b/src/Concerns/Model.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Foundation; +namespace Timatic\SDK\Concerns; use Illuminate\Support\Str; use ReflectionClass; diff --git a/src/Foundation/ModelInterface.php b/src/Concerns/ModelInterface.php similarity index 85% rename from src/Foundation/ModelInterface.php rename to src/Concerns/ModelInterface.php index 15f6db6..112c038 100644 --- a/src/Foundation/ModelInterface.php +++ b/src/Concerns/ModelInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Foundation; +namespace Timatic\SDK\Concerns; interface ModelInterface { diff --git a/src/Dto/Approve.php b/src/Dto/Approve.php index c64e5c3..d2751f8 100644 --- a/src/Dto/Approve.php +++ b/src/Dto/Approve.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Approve extends Model { diff --git a/src/Dto/Budget.php b/src/Dto/Budget.php index b377d81..1813721 100644 --- a/src/Dto/Budget.php +++ b/src/Dto/Budget.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Budget extends Model { diff --git a/src/Dto/BudgetTimeSpentTotal.php b/src/Dto/BudgetTimeSpentTotal.php index b35d22e..a274c30 100644 --- a/src/Dto/BudgetTimeSpentTotal.php +++ b/src/Dto/BudgetTimeSpentTotal.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class BudgetTimeSpentTotal extends Model { diff --git a/src/Dto/BudgetType.php b/src/Dto/BudgetType.php index aac2c09..b028577 100644 --- a/src/Dto/BudgetType.php +++ b/src/Dto/BudgetType.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class BudgetType extends Model { diff --git a/src/Dto/Correction.php b/src/Dto/Correction.php index 8a40385..c4d8cc7 100644 --- a/src/Dto/Correction.php +++ b/src/Dto/Correction.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Correction extends Model { diff --git a/src/Dto/Customer.php b/src/Dto/Customer.php index 31dc3c3..87a1d1d 100644 --- a/src/Dto/Customer.php +++ b/src/Dto/Customer.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Customer extends Model { diff --git a/src/Dto/DailyProgress.php b/src/Dto/DailyProgress.php index d29fa3c..d9015a4 100644 --- a/src/Dto/DailyProgress.php +++ b/src/Dto/DailyProgress.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class DailyProgress extends Model { diff --git a/src/Dto/Entry.php b/src/Dto/Entry.php index 17db8df..c1d17de 100644 --- a/src/Dto/Entry.php +++ b/src/Dto/Entry.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Entry extends Model { diff --git a/src/Dto/EntrySuggestion.php b/src/Dto/EntrySuggestion.php index 5b3030c..8b79dfa 100644 --- a/src/Dto/EntrySuggestion.php +++ b/src/Dto/EntrySuggestion.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class EntrySuggestion extends Model { diff --git a/src/Dto/Event.php b/src/Dto/Event.php index d883d0d..43dbca1 100644 --- a/src/Dto/Event.php +++ b/src/Dto/Event.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Event extends Model { diff --git a/src/Dto/ExportMail.php b/src/Dto/ExportMail.php index 490a2b4..e574dd3 100644 --- a/src/Dto/ExportMail.php +++ b/src/Dto/ExportMail.php @@ -2,6 +2,6 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class ExportMail extends Model {} diff --git a/src/Dto/MarkAsExported.php b/src/Dto/MarkAsExported.php index 81da941..48d8b2d 100644 --- a/src/Dto/MarkAsExported.php +++ b/src/Dto/MarkAsExported.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class MarkAsExported extends Model { diff --git a/src/Dto/MarkAsInvoiced.php b/src/Dto/MarkAsInvoiced.php index df70d85..12b1771 100644 --- a/src/Dto/MarkAsInvoiced.php +++ b/src/Dto/MarkAsInvoiced.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class MarkAsInvoiced extends Model { diff --git a/src/Dto/Overtime.php b/src/Dto/Overtime.php index a148830..63bdc1c 100644 --- a/src/Dto/Overtime.php +++ b/src/Dto/Overtime.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Overtime extends Model { diff --git a/src/Dto/Team.php b/src/Dto/Team.php index aacb600..1ae0b0e 100644 --- a/src/Dto/Team.php +++ b/src/Dto/Team.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class Team extends Model { diff --git a/src/Dto/TimeSpentTotal.php b/src/Dto/TimeSpentTotal.php index e5b3284..62ab77d 100644 --- a/src/Dto/TimeSpentTotal.php +++ b/src/Dto/TimeSpentTotal.php @@ -4,7 +4,7 @@ use Timatic\SDK\Attributes\DateTime; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class TimeSpentTotal extends Model { diff --git a/src/Dto/User.php b/src/Dto/User.php index 76cdf03..6d4372e 100644 --- a/src/Dto/User.php +++ b/src/Dto/User.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class User extends Model { diff --git a/src/Dto/UserCustomerHoursAggregate.php b/src/Dto/UserCustomerHoursAggregate.php index 79a502b..210468c 100644 --- a/src/Dto/UserCustomerHoursAggregate.php +++ b/src/Dto/UserCustomerHoursAggregate.php @@ -3,7 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Attributes\Property; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; class UserCustomerHoursAggregate extends Model { diff --git a/src/Requests/Approve/PostOvertimeApproveRequest.php b/src/Requests/Approve/PostOvertimeApproveRequest.php index 7d57b41..5777ac7 100644 --- a/src/Requests/Approve/PostOvertimeApproveRequest.php +++ b/src/Requests/Approve/PostOvertimeApproveRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postOvertimeApprove @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $overtimeId, diff --git a/src/Requests/Budget/PatchBudgetRequest.php b/src/Requests/Budget/PatchBudgetRequest.php index 544d8c4..cb688b5 100644 --- a/src/Requests/Budget/PatchBudgetRequest.php +++ b/src/Requests/Budget/PatchBudgetRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * patchBudget @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $budgetId, diff --git a/src/Requests/Budget/PostBudgetsRequest.php b/src/Requests/Budget/PostBudgetsRequest.php index 3fb7182..9b9f5cd 100644 --- a/src/Requests/Budget/PostBudgetsRequest.php +++ b/src/Requests/Budget/PostBudgetsRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postBudgets @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Correction/PatchCorrectionRequest.php b/src/Requests/Correction/PatchCorrectionRequest.php index 2d019fc..69e2464 100644 --- a/src/Requests/Correction/PatchCorrectionRequest.php +++ b/src/Requests/Correction/PatchCorrectionRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * patchCorrection @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $correctionId, diff --git a/src/Requests/Correction/PostCorrectionsRequest.php b/src/Requests/Correction/PostCorrectionsRequest.php index 34367f3..3421db4 100644 --- a/src/Requests/Correction/PostCorrectionsRequest.php +++ b/src/Requests/Correction/PostCorrectionsRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postCorrections @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Customer/PatchCustomerRequest.php b/src/Requests/Customer/PatchCustomerRequest.php index 42b28d6..e0b1607 100644 --- a/src/Requests/Customer/PatchCustomerRequest.php +++ b/src/Requests/Customer/PatchCustomerRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * patchCustomer @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $customerId, diff --git a/src/Requests/Customer/PostCustomersRequest.php b/src/Requests/Customer/PostCustomersRequest.php index c94a5e9..59075e1 100644 --- a/src/Requests/Customer/PostCustomersRequest.php +++ b/src/Requests/Customer/PostCustomersRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postCustomers @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Entry/PatchEntryRequest.php b/src/Requests/Entry/PatchEntryRequest.php index 9512f3b..a827432 100644 --- a/src/Requests/Entry/PatchEntryRequest.php +++ b/src/Requests/Entry/PatchEntryRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * patchEntry @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $entryId, diff --git a/src/Requests/Entry/PostEntriesRequest.php b/src/Requests/Entry/PostEntriesRequest.php index 3fb2e9c..8a2809e 100644 --- a/src/Requests/Entry/PostEntriesRequest.php +++ b/src/Requests/Entry/PostEntriesRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postEntries @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Event/PostEventsRequest.php b/src/Requests/Event/PostEventsRequest.php index a956f33..266ac56 100644 --- a/src/Requests/Event/PostEventsRequest.php +++ b/src/Requests/Event/PostEventsRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postEvents @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php index 02dee50..531b0bc 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postOvertimeMarkAsExported @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $overtimeId, diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php index 78a262a..7766570 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postEntryMarkAsInvoiced @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $entryId, diff --git a/src/Requests/Team/PatchTeamRequest.php b/src/Requests/Team/PatchTeamRequest.php index 041e999..12a9c36 100644 --- a/src/Requests/Team/PatchTeamRequest.php +++ b/src/Requests/Team/PatchTeamRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * patchTeam @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $teamId, diff --git a/src/Requests/Team/PostTeamsRequest.php b/src/Requests/Team/PostTeamsRequest.php index 86db651..2ea80a4 100644 --- a/src/Requests/Team/PostTeamsRequest.php +++ b/src/Requests/Team/PostTeamsRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postTeams @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/User/PatchUserRequest.php b/src/Requests/User/PatchUserRequest.php index 96975b2..84b69ca 100644 --- a/src/Requests/User/PatchUserRequest.php +++ b/src/Requests/User/PatchUserRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * patchUser @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected string $userId, diff --git a/src/Requests/User/PostUsersRequest.php b/src/Requests/User/PostUsersRequest.php index 3793b47..34110a1 100644 --- a/src/Requests/User/PostUsersRequest.php +++ b/src/Requests/User/PostUsersRequest.php @@ -6,7 +6,7 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; /** * postUsers @@ -23,7 +23,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Foundation\Model|array|null $data Request data + * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php index 3d871a0..ac36a9d 100644 --- a/src/Resource/Approve.php +++ b/src/Resource/Approve.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Approve\PostOvertimeApproveRequest; class Approve extends BaseResource { /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postOvertimeApprove(string $overtimeId, Model|array|null $data = null): Response { diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index 33ae734..dae659b 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Budget\DeleteBudgetRequest; use Timatic\SDK\Requests\Budget\GetBudgetRequest; use Timatic\SDK\Requests\Budget\GetBudgetsRequest; @@ -39,7 +39,7 @@ public function getBudgets( } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postBudgets(Model|array|null $data = null): Response { @@ -57,7 +57,7 @@ public function deleteBudget(string $budgetId): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function patchBudget(string $budgetId, Model|array|null $data = null): Response { diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index 232f47a..0dc3595 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -4,14 +4,14 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Correction\PatchCorrectionRequest; use Timatic\SDK\Requests\Correction\PostCorrectionsRequest; class Correction extends BaseResource { /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postCorrections(Model|array|null $data = null): Response { @@ -19,7 +19,7 @@ public function postCorrections(Model|array|null $data = null): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function patchCorrection(string $correctionId, Model|array|null $data = null): Response { diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index a786530..6734c5f 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Customer\DeleteCustomerRequest; use Timatic\SDK\Requests\Customer\GetCustomerRequest; use Timatic\SDK\Requests\Customer\GetCustomersRequest; @@ -19,7 +19,7 @@ public function getCustomers(?string $filterexternalId = null, ?string $filterex } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postCustomers(Model|array|null $data = null): Response { @@ -37,7 +37,7 @@ public function deleteCustomer(string $customerId): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function patchCustomer(string $customerId, Model|array|null $data = null): Response { diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 1145fd0..3bcc347 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Entry\DeleteEntryRequest; use Timatic\SDK\Requests\Entry\GetEntriesRequest; use Timatic\SDK\Requests\Entry\GetEntryRequest; @@ -87,7 +87,7 @@ public function getEntries( } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postEntries(Model|array|null $data = null): Response { @@ -105,7 +105,7 @@ public function deleteEntry(string $entryId): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function patchEntry(string $entryId, Model|array|null $data = null): Response { diff --git a/src/Resource/Event.php b/src/Resource/Event.php index 7842730..b1ccf90 100644 --- a/src/Resource/Event.php +++ b/src/Resource/Event.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Event\PostEventsRequest; class Event extends BaseResource { /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postEvents(Model|array|null $data = null): Response { diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php index 15cc7c1..d120fc0 100644 --- a/src/Resource/MarkAsExported.php +++ b/src/Resource/MarkAsExported.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\MarkAsExported\PostOvertimeMarkAsExportedRequest; class MarkAsExported extends BaseResource { /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postOvertimeMarkAsExported(string $overtimeId, Model|array|null $data = null): Response { diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php index 8e74fba..aa1986b 100644 --- a/src/Resource/MarkAsInvoiced.php +++ b/src/Resource/MarkAsInvoiced.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\MarkAsInvoiced\PostEntryMarkAsInvoicedRequest; class MarkAsInvoiced extends BaseResource { /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postEntryMarkAsInvoiced(string $entryId, Model|array|null $data = null): Response { diff --git a/src/Resource/Team.php b/src/Resource/Team.php index 3838a4f..c9b7541 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\Team\DeleteTeamRequest; use Timatic\SDK\Requests\Team\GetTeamRequest; use Timatic\SDK\Requests\Team\GetTeamsRequest; @@ -19,7 +19,7 @@ public function getTeams(): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postTeams(Model|array|null $data = null): Response { @@ -37,7 +37,7 @@ public function deleteTeam(string $teamId): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function patchTeam(string $teamId, Model|array|null $data = null): Response { diff --git a/src/Resource/User.php b/src/Resource/User.php index ce83640..b1bcfa2 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Foundation\Model; +use Timatic\SDK\Concerns\Model; use Timatic\SDK\Requests\User\DeleteUserRequest; use Timatic\SDK\Requests\User\GetUserRequest; use Timatic\SDK\Requests\User\GetUsersRequest; @@ -19,7 +19,7 @@ public function getUsers(?string $filterexternalId = null, ?string $filterextern } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function postUsers(Model|array|null $data = null): Response { @@ -37,7 +37,7 @@ public function deleteUser(string $userId): Response } /** - * @param Timatic\SDK\Foundation\Model|array|null $data Request data + * @param Timatic\SDK\Concerns\Model|array|null $data Request data */ public function patchUser(string $userId, Model|array|null $data = null): Response { From c83850ceab3c11faff205596d291664dc63492a2 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 16:57:49 +0100 Subject: [PATCH 26/62] fluent filters testing --- claude-todo.md | 57 +++++- generator/CollectionRequestTestGenerator.php | 179 ++++++++++++++++++ generator/JsonApiPestTestGenerator.php | 33 +++- .../stubs/pest-filter-assertion-block.stub | 9 + .../stubs/pest-fluent-filter-test-func.stub | 15 ++ generator/stubs/pest-resource-test.stub | 1 + tests/Requests/BudgetTest.php | 40 ++-- tests/Requests/BudgetTimeSpentTotalTest.php | 18 +- tests/Requests/BudgetTypeTest.php | 4 +- tests/Requests/ChangeTest.php | 4 +- tests/Requests/CustomerTest.php | 18 +- tests/Requests/DailyProgressTest.php | 4 +- tests/Requests/EntrySuggestionTest.php | 24 ++- tests/Requests/EntryTest.php | 88 ++------- tests/Requests/ExportMailTest.php | 4 +- tests/Requests/IncidentTest.php | 4 +- tests/Requests/MeTest.php | 4 +- tests/Requests/OvertimeTest.php | 46 ++--- tests/Requests/TeamTest.php | 4 +- tests/Requests/TimeSpentTotalTest.php | 24 ++- .../UserCustomerHoursAggregateTest.php | 52 ++--- tests/Requests/UserTest.php | 18 +- 22 files changed, 440 insertions(+), 210 deletions(-) create mode 100644 generator/CollectionRequestTestGenerator.php create mode 100644 generator/stubs/pest-filter-assertion-block.stub create mode 100644 generator/stubs/pest-fluent-filter-test-func.stub diff --git a/claude-todo.md b/claude-todo.md index 23888a0..9bf9116 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -1,15 +1,52 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. -2. De classes moeten een {modelName}Id parameter hebbben. -- Hiervoor moeten we de openapi spec file verbeteren. -- Vermoedelijk door scramble te gebruiken in Timatic API +## Task 1: Use Fluent Filters in GET Collection Tests -8. Documentation Updates +**Goal:** Replace 80+ filter constructor parameters with fluent filter chain -- Update README.md met alle nieuwe features -- Voeg voorbeelden toe voor: -- Pagination usage -- Custom response methods -- Configuration options -- Update regenerate instructies met nieuwe patterns +**Changes:** +- Modify `JsonApiPestTestGenerator.php` to detect GET collection requests (Paginatable interface) +- Add `generateFilterChain()` method - creates 2-3 representative filter examples +- Update test stub to use: `$request = (new GetEntriesRequest())->filter('userId', + 'test-123')->filter('startedAt', '2025-01-01', Operator::GreaterThanOrEquals)` +- Add proper imports (Operator enum) +- Regenerate with `composer regenerate` and test with `composer test` + +## Task 2: Replace Fixtures with Inline Mock Data + +**Goal:** Remove fixture dependencies, use inline `MockResponse::make()` + +**Changes:** +- Add `generateMockResponseBody()` method - creates JSON:API structure from endpoint schema +- Generate realistic test data based on property types +- Use `MockResponse::make(body: ['data' => [...]])` instead of `MockResponse::fixture()` +- Handle both single resource and collection responses +- Regenerate with `composer regenerate` and test with `composer test` + +## Task 3: Validate DTO Hydration in GET Tests + +**Goal:** Verify JSON:API responses properly populate DTOs + +**Changes:** +- Add expectations after response: `expect($data['type'])->toBe('entries')` +- Validate `data.id`, `data.attributes` structure +- Check 2-3 key DTO properties are present with expected values +- Regenerate with `composer regenerate` and test with `composer test` + +## Task 4: Validate JSON:API Request Bodies in POST/PATCH Tests + +**Goal:** Verify DTOs serialize to proper JSON:API format + +**Changes:** +- Create DTO instance with test data in test +- Pass DTO to POST/PATCH method +- Use `Saloon::assertSent(function ($request) { ... })` callback +- Validate `data.type`, `data.attributes` structure +- Check specific attribute values match DTO input +- Regenerate with `composer regenerate` and test with `composer test` + + +## TASK 5: Add params for filters in Resources + +**Note:** Each task is independent and can be completed, tested, and committed separately. \ No newline at end of file diff --git a/generator/CollectionRequestTestGenerator.php b/generator/CollectionRequestTestGenerator.php new file mode 100644 index 0000000..20920a9 --- /dev/null +++ b/generator/CollectionRequestTestGenerator.php @@ -0,0 +1,179 @@ +method->isGet() && empty($endpoint->pathParameters); + } + + /** + * Get the stub path for collection request tests + */ + public function getStubPath(): string + { + return __DIR__.'/stubs/pest-fluent-filter-test-func.stub'; + } + + /** + * Replace stub variables with collection-specific content + */ + public function replaceStubVariables(string $functionStub, Endpoint $endpoint): string + { + $filterData = $this->generateFilterChainWithData($endpoint); + $functionStub = str_replace('{{ filterChain }}', $filterData['chain'], $functionStub); + + // Only include filter assertions block if there are filters + if (! empty($filterData['assertions'])) { + $filterAssertionBlock = $this->generateFilterAssertionBlock($filterData['assertions']); + $functionStub = str_replace('{{ filterAssertionBlock }}', $filterAssertionBlock, $functionStub); + } else { + $functionStub = str_replace('{{ filterAssertionBlock }}', '', $functionStub); + } + + // Add non-filter query parameters (like 'include') + $nonFilterParams = $this->getNonFilterQueryParameters($endpoint); + $functionStub = str_replace('{{ nonFilterParams }}', $nonFilterParams, $functionStub); + + return $functionStub; + } + + /** + * Generate the complete filter assertion block + */ + protected function generateFilterAssertionBlock(string $assertions): string + { + $stub = file_get_contents(__DIR__.'/stubs/pest-filter-assertion-block.stub'); + + return str_replace('{{ filterAssertions }}', $assertions, $stub); + } + + /** + * Generate a fluent filter chain with 2-3 representative examples and their assertions + */ + protected function generateFilterChainWithData(Endpoint $endpoint): array + { + $filters = []; + $assertions = []; + $maxFilters = 3; + $seenProperties = []; + + // Extract filter parameters from query parameters + foreach ($endpoint->queryParameters as $parameter) { + if (count($filters) >= $maxFilters) { + break; + } + + // Skip non-filter parameters + if (! str_starts_with($parameter->name, 'filter[')) { + continue; + } + + // Parse filter[property][operator] or filter[property] + preg_match('/filter\[([^\]]+)\](?:\[([^\]]+)\])?/', $parameter->name, $matches); + $property = $matches[1] ?? null; + $operator = $matches[2] ?? null; + + if (! $property) { + continue; + } + + // Skip if we already have a filter for this property (avoid duplicates with operators) + if (isset($seenProperties[$property])) { + continue; + } + + // Only add filters without operators (simpler) + if (! $operator && count($filters) < $maxFilters) { + $value = $this->generateTestValueForProperty($property); + $filters[] = "->filter('{$property}', {$value})"; + + // Generate assertion for this filter + $assertions[] = $this->generateFilterAssertion($property, $value); + + $seenProperties[$property] = true; + } + } + + return [ + 'chain' => implode("\n\t\t", $filters), + 'assertions' => implode("\n\t\t", $assertions), + ]; + } + + /** + * Generate a filter assertion for the given property and value + */ + protected function generateFilterAssertion(string $property, string $value): string + { + // Handle both string and boolean values + if ($value === 'true' || $value === 'false') { + // Boolean values + return "expect(\$query)->toHaveKey('filter[{$property}]', {$value});"; + } + + // String values - remove quotes for the assertion + $assertionValue = trim($value, "'"); + + return "expect(\$query)->toHaveKey('filter[{$property}]', '{$assertionValue}');"; + } + + /** + * Get non-filter query parameters (like 'include') + */ + protected function getNonFilterQueryParameters(Endpoint $endpoint): string + { + $params = []; + + foreach ($endpoint->queryParameters as $parameter) { + if (! str_starts_with($parameter->name, 'filter[')) { + $paramName = NameHelper::safeVariableName($parameter->name); + $value = match ($parameter->type) { + 'string' => "'test string'", + 'int', 'integer' => '123', + 'bool', 'boolean' => 'true', + 'array' => '[]', + default => 'null', + }; + $params[] = "{$paramName}: {$value}"; + } + } + + return implode(', ', $params); + } + + /** + * Generate appropriate test value based on property name + */ + protected function generateTestValueForProperty(string $property): string + { + // Date/time properties + if (str_contains($property, 'At') || str_contains($property, 'Date')) { + return "'2025-01-01'"; + } + + // ID properties + if (str_ends_with($property, 'Id')) { + return "'test-id-123'"; + } + + // Boolean properties + if (str_starts_with($property, 'is') || str_starts_with($property, 'has')) { + return 'true'; + } + + // Default to string + return "'test-value'"; + } +} diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index cbbd7cc..3d91cc8 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -11,6 +11,13 @@ class JsonApiPestTestGenerator extends PestTestGenerator { + protected CollectionRequestTestGenerator $collectionTestGenerator; + + public function __construct() + { + $this->collectionTestGenerator = new CollectionRequestTestGenerator; + } + /** * Skip generating Pest.php - we have a custom version */ @@ -40,7 +47,7 @@ protected function shouldIncludeEndpoint(Endpoint $endpoint): bool */ protected function getTestStubPath(): string { - return __DIR__.'/Stubs/pest-resource-test.stub'; + return __DIR__.'/stubs/pest-resource-test.stub'; } /** @@ -48,7 +55,12 @@ protected function getTestStubPath(): string */ protected function getTestFunctionStubPath(Endpoint $endpoint): string { - return __DIR__.'/Stubs/pest-resource-test-func.stub'; + // Delegate to CollectionRequestTestGenerator for collection requests + if ($this->collectionTestGenerator->isCollectionRequest($endpoint)) { + return $this->collectionTestGenerator->getStubPath(); + } + + return __DIR__.'/stubs/pest-resource-test-func.stub'; } /** @@ -97,4 +109,21 @@ protected function getTestParameterName(Parameter $parameter, Endpoint $endpoint return $name; } + + /** + * Hook: Replace additional stub variables + */ + protected function replaceAdditionalStubVariables( + string $functionStub, + Endpoint $endpoint, + string $resourceName, + string $requestClassName + ): string { + // Delegate to CollectionRequestTestGenerator for collection requests + if ($this->collectionTestGenerator->isCollectionRequest($endpoint)) { + return $this->collectionTestGenerator->replaceStubVariables($functionStub, $endpoint); + } + + return $functionStub; + } } diff --git a/generator/stubs/pest-filter-assertion-block.stub b/generator/stubs/pest-filter-assertion-block.stub new file mode 100644 index 0000000..c28e09e --- /dev/null +++ b/generator/stubs/pest-filter-assertion-block.stub @@ -0,0 +1,9 @@ + + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + {{ filterAssertions }} + + return true; + }); diff --git a/generator/stubs/pest-fluent-filter-test-func.stub b/generator/stubs/pest-fluent-filter-test-func.stub new file mode 100644 index 0000000..f0b2801 --- /dev/null +++ b/generator/stubs/pest-fluent-filter-test-func.stub @@ -0,0 +1,15 @@ +it('{{ testDescription }}', function () { + Saloon::fake([ + {{ requestClass }}::class => MockResponse::fixture('{{ fixtureName }}'), + ]); + + $request = (new {{ requestClass }}({{ nonFilterParams }})) +{{ filterChain }}; + + $response = $this->{{ clientName }}->send($request); + + Saloon::assertSent({{ requestClass }}::class); +{{ filterAssertionBlock }} + + expect($response->status())->toBe(200); +}); diff --git a/generator/stubs/pest-resource-test.stub b/generator/stubs/pest-resource-test.stub index 5560edf..9959b17 100644 --- a/generator/stubs/pest-resource-test.stub +++ b/generator/stubs/pest-resource-test.stub @@ -3,6 +3,7 @@ {{ dtoImports }} {{ requestImports }} use Saloon\Http\Faking\MockResponse; +use Saloon\Http\Request; use Saloon\Laravel\Facades\Saloon; beforeEach(function () { diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index d31944b..b74f0eb 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('budget.getBudgets'), ]); - $response = $this->timaticConnector->budget()->getBudgets( - filtercustomerId: 123, - filtercustomerIdeq: 123, - filtercustomerIdnq: 123, - filtercustomerIdgt: 123, - filtercustomerIdlt: 123, - filtercustomerIdgte: 123, - filtercustomerIdlte: 123, - filtercustomerIdcontains: 123, - filterbudgetTypeId: 'test string', - filterbudgetTypeIdeq: 'test string', - filterbudgetTypeIdnq: 'test string', - filterbudgetTypeIdgt: 'test string', - filterbudgetTypeIdlt: 'test string', - filterbudgetTypeIdgte: 'test string', - filterbudgetTypeIdlte: 'test string', - filterbudgetTypeIdcontains: 'test string', - filterisArchived: 'test string', - filtercustomerExternalId: 'test string', - filtershowToCustomer: 'test string', - include: 'test string' - ); + $request = (new GetBudgetsRequest(include: 'test string')) + ->filter('customerId', 'test-id-123') + ->filter('budgetTypeId', 'test-id-123') + ->filter('isArchived', true); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetBudgetsRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[customerId]', 'test-id-123'); + expect($query)->toHaveKey('filter[budgetTypeId]', 'test-id-123'); + expect($query)->toHaveKey('filter[isArchived]', true); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/BudgetTimeSpentTotalTest.php b/tests/Requests/BudgetTimeSpentTotalTest.php index 519d09e..ce25172 100644 --- a/tests/Requests/BudgetTimeSpentTotalTest.php +++ b/tests/Requests/BudgetTimeSpentTotalTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('budgetTimeSpentTotal.getBudgetTimeSpentTotals'), ]); - $response = $this->timaticConnector->budgetTimeSpentTotal()->getBudgetTimeSpentTotals( - filterbudgetId: 'test string', - filterbudgetIdeq: 'test string' - ); + $request = (new GetBudgetTimeSpentTotalsRequest) + ->filter('budgetId', 'test-id-123'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetBudgetTimeSpentTotalsRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[budgetId]', 'test-id-123'); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/BudgetTypeTest.php b/tests/Requests/BudgetTypeTest.php index 335f86a..03d0082 100644 --- a/tests/Requests/BudgetTypeTest.php +++ b/tests/Requests/BudgetTypeTest.php @@ -13,9 +13,9 @@ GetBudgetTypesRequest::class => MockResponse::fixture('budgetType.getBudgetTypes'), ]); - $response = $this->timaticConnector->budgetType()->getBudgetTypes( + $request = (new GetBudgetTypesRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetBudgetTypesRequest::class); diff --git a/tests/Requests/ChangeTest.php b/tests/Requests/ChangeTest.php index c903f7c..98079fc 100644 --- a/tests/Requests/ChangeTest.php +++ b/tests/Requests/ChangeTest.php @@ -28,9 +28,9 @@ GetChangesRequest::class => MockResponse::fixture('change.getChanges'), ]); - $response = $this->timaticConnector->change()->getChanges( + $request = (new GetChangesRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetChangesRequest::class); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index d3d1a88..a1dd088 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('customer.getCustomers'), ]); - $response = $this->timaticConnector->customer()->getCustomers( - filterexternalId: 'test string', - filterexternalIdeq: 'test string' - ); + $request = (new GetCustomersRequest) + ->filter('externalId', 'test-id-123'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetCustomersRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[externalId]', 'test-id-123'); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/DailyProgressTest.php b/tests/Requests/DailyProgressTest.php index 5912bee..79bf9fc 100644 --- a/tests/Requests/DailyProgressTest.php +++ b/tests/Requests/DailyProgressTest.php @@ -13,9 +13,9 @@ GetDailyProgressesRequest::class => MockResponse::fixture('dailyProgress.getDailyProgresses'), ]); - $response = $this->timaticConnector->dailyProgress()->getDailyProgresses( + $request = (new GetDailyProgressesRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetDailyProgressesRequest::class); diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index 62cbe24..ef2ec5e 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('entrySuggestion.getEntrySuggestions'), ]); - $response = $this->timaticConnector->entrySuggestion()->getEntrySuggestions( - filterdate: 'test string', - filterdateeq: 'test string', - filterdatenq: 'test string', - filterdategt: 'test string', - filterdatelt: 'test string', - filterdategte: 'test string', - filterdatelte: 'test string', - filterdatecontains: 'test string' - ); + $request = (new GetEntrySuggestionsRequest) + ->filter('date', 'test-value'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetEntrySuggestionsRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[date]', 'test-value'); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 6cdbef7..83a6f86 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('entry.getEntries'), ]); - $response = $this->timaticConnector->entry()->getEntries( - filteruserId: 'test string', - filteruserIdeq: 'test string', - filteruserIdnq: 'test string', - filteruserIdgt: 'test string', - filteruserIdlt: 'test string', - filteruserIdgte: 'test string', - filteruserIdlte: 'test string', - filteruserIdcontains: 'test string', - filterbudgetId: 'test string', - filterbudgetIdeq: 'test string', - filterbudgetIdnq: 'test string', - filterbudgetIdgt: 'test string', - filterbudgetIdlt: 'test string', - filterbudgetIdgte: 'test string', - filterbudgetIdlte: 'test string', - filterbudgetIdcontains: 'test string', - filterstartedAt: 'test string', - filterstartedAteq: 'test string', - filterstartedAtnq: 'test string', - filterstartedAtgt: 'test string', - filterstartedAtlt: 'test string', - filterstartedAtgte: 'test string', - filterstartedAtlte: 'test string', - filterstartedAtcontains: 'test string', - filterendedAt: 'test string', - filterendedAteq: 'test string', - filterendedAtnq: 'test string', - filterendedAtgt: 'test string', - filterendedAtlt: 'test string', - filterendedAtgte: 'test string', - filterendedAtlte: 'test string', - filterendedAtcontains: 'test string', - filterhasOvertime: 'test string', - filterhasOvertimeeq: 'test string', - filterhasOvertimenq: 'test string', - filterhasOvertimegt: 'test string', - filterhasOvertimelt: 'test string', - filterhasOvertimegte: 'test string', - filterhasOvertimelte: 'test string', - filterhasOvertimecontains: 'test string', - filteruserFullName: 'test string', - filteruserFullNameeq: 'test string', - filteruserFullNamenq: 'test string', - filteruserFullNamegt: 'test string', - filteruserFullNamelt: 'test string', - filteruserFullNamegte: 'test string', - filteruserFullNamelte: 'test string', - filteruserFullNamecontains: 'test string', - filtercustomerId: 'test string', - filtercustomerIdeq: 'test string', - filtercustomerIdnq: 'test string', - filtercustomerIdgt: 'test string', - filtercustomerIdlt: 'test string', - filtercustomerIdgte: 'test string', - filtercustomerIdlte: 'test string', - filtercustomerIdcontains: 'test string', - filterticketNumber: 'test string', - filterticketNumbereq: 'test string', - filterticketNumbernq: 'test string', - filterticketNumbergt: 'test string', - filterticketNumberlt: 'test string', - filterticketNumbergte: 'test string', - filterticketNumberlte: 'test string', - filterticketNumbercontains: 'test string', - filtersettlement: 'test string', - filterisInvoiced: 'test string', - filterisInvoiceable: 'test string', - include: 'test string' - ); + $request = (new GetEntriesRequest(include: 'test string')) + ->filter('userId', 'test-id-123') + ->filter('budgetId', 'test-id-123') + ->filter('startedAt', '2025-01-01'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetEntriesRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[userId]', 'test-id-123'); + expect($query)->toHaveKey('filter[budgetId]', 'test-id-123'); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-01'); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/ExportMailTest.php b/tests/Requests/ExportMailTest.php index 8173c4f..bebeace 100644 --- a/tests/Requests/ExportMailTest.php +++ b/tests/Requests/ExportMailTest.php @@ -13,9 +13,9 @@ GetBudgetsExportMailsRequest::class => MockResponse::fixture('exportMail.getBudgetsExportMails'), ]); - $response = $this->timaticConnector->exportMail()->getBudgetsExportMails( + $request = (new GetBudgetsExportMailsRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetBudgetsExportMailsRequest::class); diff --git a/tests/Requests/IncidentTest.php b/tests/Requests/IncidentTest.php index 936818f..37a34aa 100644 --- a/tests/Requests/IncidentTest.php +++ b/tests/Requests/IncidentTest.php @@ -28,9 +28,9 @@ GetIncidentsRequest::class => MockResponse::fixture('incident.getIncidents'), ]); - $response = $this->timaticConnector->incident()->getIncidents( + $request = (new GetIncidentsRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetIncidentsRequest::class); diff --git a/tests/Requests/MeTest.php b/tests/Requests/MeTest.php index 580713d..4896170 100644 --- a/tests/Requests/MeTest.php +++ b/tests/Requests/MeTest.php @@ -13,9 +13,9 @@ GetMesRequest::class => MockResponse::fixture('me.getMes'), ]); - $response = $this->timaticConnector->me()->getMes( + $request = (new GetMesRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetMesRequest::class); diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index 9d97c8a..d3574ab 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('overtime.getOvertimes'), ]); - $response = $this->timaticConnector->overtime()->getOvertimes( - filterstartedAt: 'test string', - filterstartedAteq: 'test string', - filterstartedAtnq: 'test string', - filterstartedAtgt: 'test string', - filterstartedAtlt: 'test string', - filterstartedAtgte: 'test string', - filterstartedAtlte: 'test string', - filterstartedAtcontains: 'test string', - filterendedAt: 'test string', - filterendedAteq: 'test string', - filterendedAtnq: 'test string', - filterendedAtgt: 'test string', - filterendedAtlt: 'test string', - filterendedAtgte: 'test string', - filterendedAtlte: 'test string', - filterendedAtcontains: 'test string', - filterisApproved: 'test string', - filterapprovedAt: 'test string', - filterapprovedAteq: 'test string', - filterapprovedAtnq: 'test string', - filterapprovedAtgt: 'test string', - filterapprovedAtlt: 'test string', - filterapprovedAtgte: 'test string', - filterapprovedAtlte: 'test string', - filterapprovedAtcontains: 'test string', - filterisExported: 'test string' - ); + $request = (new GetOvertimesRequest) + ->filter('startedAt', '2025-01-01') + ->filter('endedAt', '2025-01-01') + ->filter('isApproved', true); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetOvertimesRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[startedAt]', '2025-01-01'); + expect($query)->toHaveKey('filter[endedAt]', '2025-01-01'); + expect($query)->toHaveKey('filter[isApproved]', true); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index 776ae94..784c54c 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -17,9 +17,9 @@ GetTeamsRequest::class => MockResponse::fixture('team.getTeams'), ]); - $response = $this->timaticConnector->team()->getTeams( + $request = (new GetTeamsRequest); - ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetTeamsRequest::class); diff --git a/tests/Requests/TimeSpentTotalTest.php b/tests/Requests/TimeSpentTotalTest.php index 5596b42..05ac368 100644 --- a/tests/Requests/TimeSpentTotalTest.php +++ b/tests/Requests/TimeSpentTotalTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('timeSpentTotal.getTimeSpentTotals'), ]); - $response = $this->timaticConnector->timeSpentTotal()->getTimeSpentTotals( - filterstartedAtgte: 'test string', - filterstartedAtlte: 'test string', - filterteamId: 'test string', - filterteamIdeq: 'test string', - filteruserId: 'test string', - filteruserIdeq: 'test string' - ); + $request = (new GetTimeSpentTotalsRequest) + ->filter('teamId', 'test-id-123') + ->filter('userId', 'test-id-123'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetTimeSpentTotalsRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[teamId]', 'test-id-123'); + expect($query)->toHaveKey('filter[userId]', 'test-id-123'); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/UserCustomerHoursAggregateTest.php b/tests/Requests/UserCustomerHoursAggregateTest.php index 9f5c293..1f244a7 100644 --- a/tests/Requests/UserCustomerHoursAggregateTest.php +++ b/tests/Requests/UserCustomerHoursAggregateTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('userCustomerHoursAggregate.getUserCustomerHoursAggregates'), ]); - $response = $this->timaticConnector->userCustomerHoursAggregate()->getUserCustomerHoursAggregates( - filterstartedAt: 'test string', - filterstartedAteq: 'test string', - filterstartedAtnq: 'test string', - filterstartedAtgt: 'test string', - filterstartedAtlt: 'test string', - filterstartedAtgte: 'test string', - filterstartedAtlte: 'test string', - filterstartedAtcontains: 'test string', - filterendedAt: 'test string', - filterendedAteq: 'test string', - filterendedAtnq: 'test string', - filterendedAtgt: 'test string', - filterendedAtlt: 'test string', - filterendedAtgte: 'test string', - filterendedAtlte: 'test string', - filterendedAtcontains: 'test string', - filterteamId: 'test string', - filterteamIdeq: 'test string', - filterteamIdnq: 'test string', - filterteamIdgt: 'test string', - filterteamIdlt: 'test string', - filterteamIdgte: 'test string', - filterteamIdlte: 'test string', - filterteamIdcontains: 'test string', - filteruserId: 'test string', - filteruserIdeq: 'test string', - filteruserIdnq: 'test string', - filteruserIdgt: 'test string', - filteruserIdlt: 'test string', - filteruserIdgte: 'test string', - filteruserIdlte: 'test string', - filteruserIdcontains: 'test string' - ); + $request = (new GetUserCustomerHoursAggregatesRequest) + ->filter('startedAt', '2025-01-01') + ->filter('endedAt', '2025-01-01') + ->filter('teamId', 'test-id-123'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetUserCustomerHoursAggregatesRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[startedAt]', '2025-01-01'); + expect($query)->toHaveKey('filter[endedAt]', '2025-01-01'); + expect($query)->toHaveKey('filter[teamId]', 'test-id-123'); + + return true; + }); + expect($response->status())->toBe(200); }); diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index e426110..ae2e174 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -1,6 +1,7 @@ MockResponse::fixture('user.getUsers'), ]); - $response = $this->timaticConnector->user()->getUsers( - filterexternalId: 'test string', - filterexternalIdeq: 'test string' - ); + $request = (new GetUsersRequest) + ->filter('externalId', 'test-id-123'); + + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetUsersRequest::class); + // Verify filter query parameters are present + Saloon::assertSent(function (Request $request) { + $query = $request->query()->all(); + + expect($query)->toHaveKey('filter[externalId]', 'test-id-123'); + + return true; + }); + expect($response->status())->toBe(200); }); From 795accd6a66cde8e99a8ae3ddbd712c6458c6bf2 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 19:47:07 +0100 Subject: [PATCH 27/62] add inline mock data to tests --- claude-todo.md | 2 + generator/JsonApiPestTestGenerator.php | 378 +++++++++++++++++- tests/Requests/ApproveTest.php | 2 +- tests/Requests/BudgetTest.php | 35 +- tests/Requests/BudgetTimeSpentTotalTest.php | 19 +- tests/Requests/BudgetTypeTest.php | 19 +- tests/Requests/ChangeTest.php | 29 +- tests/Requests/CorrectionTest.php | 4 +- tests/Requests/CustomerTest.php | 35 +- tests/Requests/DailyProgressTest.php | 19 +- tests/Requests/EntriesExportTest.php | 10 +- tests/Requests/EntrySuggestionTest.php | 31 +- tests/Requests/EntryTest.php | 35 +- tests/Requests/EventTest.php | 2 +- tests/Requests/ExportMailTest.php | 19 +- tests/Requests/IncidentTest.php | 29 +- tests/Requests/MarkAsExportedTest.php | 2 +- tests/Requests/MarkAsInvoicedTest.php | 2 +- tests/Requests/MeTest.php | 19 +- tests/Requests/NumberTest.php | 10 +- tests/Requests/OvertimeTest.php | 19 +- tests/Requests/PeriodTest.php | 10 +- tests/Requests/TeamTest.php | 35 +- tests/Requests/TimeSpentTotalTest.php | 19 +- .../UserCustomerHoursAggregateTest.php | 19 +- tests/Requests/UserTest.php | 35 +- 26 files changed, 788 insertions(+), 50 deletions(-) diff --git a/claude-todo.md b/claude-todo.md index 9bf9116..12dad2b 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -47,6 +47,8 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid - Regenerate with `composer regenerate` and test with `composer test` +## TASK: replace "type: resource" with actual resource in mocked GET data + ## TASK 5: Add params for filters in Resources **Note:** Each task is independent and can be completed, tested, and committed separately. \ No newline at end of file diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 3d91cc8..bc176c9 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -13,11 +13,30 @@ class JsonApiPestTestGenerator extends PestTestGenerator { protected CollectionRequestTestGenerator $collectionTestGenerator; + protected ?array $openApiSpec = null; + public function __construct() { $this->collectionTestGenerator = new CollectionRequestTestGenerator; } + /** + * Load and cache the OpenAPI specification + */ + protected function getOpenApiSpec(): array + { + if ($this->openApiSpec === null) { + $specPath = __DIR__.'/../openapi.json'; + if (file_exists($specPath)) { + $this->openApiSpec = json_decode(file_get_contents($specPath), true); + } else { + $this->openApiSpec = []; + } + } + + return $this->openApiSpec; + } + /** * Skip generating Pest.php - we have a custom version */ @@ -121,9 +140,366 @@ protected function replaceAdditionalStubVariables( ): string { // Delegate to CollectionRequestTestGenerator for collection requests if ($this->collectionTestGenerator->isCollectionRequest($endpoint)) { - return $this->collectionTestGenerator->replaceStubVariables($functionStub, $endpoint); + $functionStub = $this->collectionTestGenerator->replaceStubVariables($functionStub, $endpoint); + } + + // Only generate mock data for GET requests + if ($endpoint->method->isGet()) { + // Replace fixture with inline mock data + // Match both the template variable and actual fixture names + $mockResponseBody = $this->generateMockResponseBody($endpoint); + $functionStub = preg_replace( + "/MockResponse::fixture\('[^']+'\)/", + "MockResponse::make($mockResponseBody, 200)", + $functionStub + ); + } else { + // For non-GET requests (POST, PATCH, DELETE), use simple 200 response + $functionStub = preg_replace( + "/MockResponse::fixture\('[^']+'\)/", + 'MockResponse::make([], 200)', + $functionStub + ); } return $functionStub; } + + /** + * Find an endpoint specification by operation ID + */ + protected function findEndpointSpecByOperationId(string $operationId): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec) || ! isset($spec['paths'])) { + return null; + } + + // Search through all paths and methods to find the matching operationId + foreach ($spec['paths'] as $path => $pathItem) { + foreach ($pathItem as $method => $operation) { + // Skip non-operation keys (like parameters, summary, etc.) + if (! is_array($operation) || ! isset($operation['operationId'])) { + continue; + } + + if ($operation['operationId'] === $operationId) { + return $operation; + } + } + } + + return null; + } + + /** + * Extract example data from OpenAPI spec for an endpoint + */ + protected function extractExampleFromSpec(Endpoint $endpoint): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec)) { + return null; + } + + // Find the endpoint spec by operationId + $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); + if (! $endpointSpec) { + return null; + } + + // Try to find examples in this order: + // 1. Response-level example: responses.200.content.application/json.example + if (isset($endpointSpec['responses']['200']['content']['application/json']['example'])) { + return $endpointSpec['responses']['200']['content']['application/json']['example']; + } + + // 2. Response-level examples array (use first one) + if (isset($endpointSpec['responses']['200']['content']['application/json']['examples'])) { + $examples = $endpointSpec['responses']['200']['content']['application/json']['examples']; + $firstExample = reset($examples); + if (isset($firstExample['value'])) { + return $firstExample['value']; + } + } + + // 3. Schema-level example + if (isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { + $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; + + // Check for direct example + if (isset($schema['example'])) { + return $schema['example']; + } + + // Check if schema references a component + if (isset($schema['$ref'])) { + $ref = $schema['$ref']; + $refPath = str_replace('#/', '', $ref); + $refParts = explode('/', $refPath); + + // Navigate to the referenced schema + $referencedSchema = $spec; + foreach ($refParts as $part) { + if (! isset($referencedSchema[$part])) { + return null; + } + $referencedSchema = $referencedSchema[$part]; + } + + // Check for example in referenced schema + if (isset($referencedSchema['example'])) { + return $referencedSchema['example']; + } + } + } + + return null; + } + + /** + * Generate mock data based on property types (fallback when no examples exist) + */ + protected function generateMockData(Endpoint $endpoint): array + { + $spec = $this->getOpenApiSpec(); + $isCollection = $this->collectionTestGenerator->isCollectionRequest($endpoint); + + // Try to determine the schema for this endpoint + $schema = $this->getResponseSchemaForEndpoint($endpoint); + + if ($schema) { + // Generate mock data based on schema + $attributes = $this->generateMockAttributes($schema); + $resourceType = $this->getResourceTypeFromSchema($schema); + + if ($isCollection) { + // Generate 2-3 items for collections + return [ + 'data' => [ + [ + 'type' => $resourceType, + 'id' => 'mock-id-1', + 'attributes' => $attributes, + ], + [ + 'type' => $resourceType, + 'id' => 'mock-id-2', + 'attributes' => $this->generateMockAttributes($schema), + ], + ], + ]; + } + + return [ + 'data' => [ + 'type' => $resourceType, + 'id' => 'mock-id-123', + 'attributes' => $attributes, + ], + ]; + } + + // Fallback: generic mock data + $resourceName = NameHelper::resourceClassName($endpoint->collection); + $resourceType = NameHelper::safeVariableName($resourceName); + + if ($isCollection) { + return [ + 'data' => [ + ['type' => $resourceType, 'id' => 'mock-id-1', 'attributes' => ['name' => 'Mock item 1']], + ['type' => $resourceType, 'id' => 'mock-id-2', 'attributes' => ['name' => 'Mock item 2']], + ], + ]; + } + + return [ + 'data' => [ + 'type' => $resourceType, + 'id' => 'mock-id-123', + 'attributes' => ['name' => 'Mock item'], + ], + ]; + } + + /** + * Get the response schema for an endpoint from the OpenAPI spec + */ + protected function getResponseSchemaForEndpoint(Endpoint $endpoint): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec)) { + return null; + } + + // Find the endpoint spec by operationId + $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); + if (! $endpointSpec || ! isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { + return null; + } + + $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; + + // Handle array responses (collections) + if (isset($schema['type']) && $schema['type'] === 'array' && isset($schema['items'])) { + $schema = $schema['items']; + } + + // Resolve $ref if present + if (isset($schema['$ref'])) { + $ref = $schema['$ref']; + $refPath = str_replace('#/', '', $ref); + $refParts = explode('/', $refPath); + + $referencedSchema = $spec; + foreach ($refParts as $part) { + if (! isset($referencedSchema[$part])) { + return null; + } + $referencedSchema = $referencedSchema[$part]; + } + + return $referencedSchema; + } + + return $schema; + } + + /** + * Generate mock attributes based on schema properties + */ + protected function generateMockAttributes(array $schema): array + { + $attributes = []; + + // Check if this is a JSON:API schema with attributes object + if (isset($schema['properties']['attributes']['properties'])) { + $properties = $schema['properties']['attributes']['properties']; + } elseif (isset($schema['properties'])) { + $properties = $schema['properties']; + } else { + return ['name' => 'Mock value']; + } + + foreach ($properties as $propName => $propSpec) { + // Skip non-attribute fields + if (in_array($propName, ['id', 'type', 'attributes', 'relationships'])) { + continue; + } + + $attributes[$propName] = $this->getMockValueForProperty($propName, $propSpec); + } + + return $attributes; + } + + /** + * Generate a mock value based on property name and type + */ + protected function getMockValueForProperty(string $propertyName, array $propertySpec): mixed + { + // Check for example in property spec + if (isset($propertySpec['example'])) { + return $propertySpec['example']; + } + + $type = $propertySpec['type'] ?? 'string'; + $format = $propertySpec['format'] ?? null; + + // DateTime fields + if ($format === 'date-time' || str_contains($propertyName, 'At') || str_contains($propertyName, 'Date')) { + return '2025-01-15T10:30:00Z'; + } + + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return 'mock-id-123'; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return 'test@example.com'; + } + + // Type-based generation + return match ($type) { + 'boolean' => true, + 'integer', 'number' => 42, + 'string' => 'Mock value', + 'array' => [], + 'object' => [], + default => 'Mock value', + }; + } + + /** + * Get the resource type from a schema (e.g., "users", "entries") + */ + protected function getResourceTypeFromSchema(array $schema): string + { + // Try to extract from schema title or description + if (isset($schema['title'])) { + return NameHelper::safeVariableName($schema['title']); + } + + // Try to get from properties.type.example + if (isset($schema['properties']['type']['example'])) { + return $schema['properties']['type']['example']; + } + + // Fallback to generic name + return 'resources'; + } + + /** + * Generate the complete mock response body for an endpoint + */ + protected function generateMockResponseBody(Endpoint $endpoint): string + { + // Try to extract example from OpenAPI spec first + $example = $this->extractExampleFromSpec($endpoint); + + if ($example !== null) { + // Use example from spec + $mockData = $example; + } else { + // Generate fallback mock data + $mockData = $this->generateMockData($endpoint); + } + + // Format as PHP array syntax for the stub + return $this->formatArrayAsPhp($mockData); + } + + /** + * Format an array as PHP code string + */ + protected function formatArrayAsPhp(array $data, int $indent = 0): string + { + $indentStr = str_repeat(' ', $indent); + $lines = []; + + foreach ($data as $key => $value) { + $keyStr = is_string($key) ? "'$key'" : $key; + + if (is_array($value)) { + $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; + } elseif (is_string($value)) { + $escapedValue = addslashes($value); + $lines[] = $indentStr." $keyStr => '$escapedValue',"; + } elseif (is_bool($value)) { + $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; + } elseif (is_null($value)) { + $lines[] = $indentStr." $keyStr => null,"; + } else { + $lines[] = $indentStr." $keyStr => $value,"; + } + } + + if (empty($lines)) { + return '[]'; + } + + return "[\n".implode("\n", $lines)."\n$indentStr]"; + } } diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index d8ff63f..80f82d0 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -10,7 +10,7 @@ it('calls the postOvertimeApprove method in the Approve resource', function () { Saloon::fake([ - PostOvertimeApproveRequest::class => MockResponse::fixture('approve.postOvertimeApprove'), + PostOvertimeApproveRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->approve()->postOvertimeApprove( diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index b74f0eb..2d5c8a5 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -15,7 +15,24 @@ it('calls the getBudgets method in the Budget resource', function () { Saloon::fake([ - GetBudgetsRequest::class => MockResponse::fixture('budget.getBudgets'), + GetBudgetsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetBudgetsRequest(include: 'test string')) @@ -43,7 +60,7 @@ it('calls the postBudgets method in the Budget resource', function () { Saloon::fake([ - PostBudgetsRequest::class => MockResponse::fixture('budget.postBudgets'), + PostBudgetsRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->budget()->postBudgets( @@ -57,7 +74,15 @@ it('calls the getBudget method in the Budget resource', function () { Saloon::fake([ - GetBudgetRequest::class => MockResponse::fixture('budget.getBudget'), + GetBudgetRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'data' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->budget()->getBudget( @@ -71,7 +96,7 @@ it('calls the deleteBudget method in the Budget resource', function () { Saloon::fake([ - DeleteBudgetRequest::class => MockResponse::fixture('budget.deleteBudget'), + DeleteBudgetRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->budget()->deleteBudget( @@ -85,7 +110,7 @@ it('calls the patchBudget method in the Budget resource', function () { Saloon::fake([ - PatchBudgetRequest::class => MockResponse::fixture('budget.patchBudget'), + PatchBudgetRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->budget()->patchBudget( diff --git a/tests/Requests/BudgetTimeSpentTotalTest.php b/tests/Requests/BudgetTimeSpentTotalTest.php index ce25172..4e5e87a 100644 --- a/tests/Requests/BudgetTimeSpentTotalTest.php +++ b/tests/Requests/BudgetTimeSpentTotalTest.php @@ -11,7 +11,24 @@ it('calls the getBudgetTimeSpentTotals method in the BudgetTimeSpentTotal resource', function () { Saloon::fake([ - GetBudgetTimeSpentTotalsRequest::class => MockResponse::fixture('budgetTimeSpentTotal.getBudgetTimeSpentTotals'), + GetBudgetTimeSpentTotalsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetBudgetTimeSpentTotalsRequest) diff --git a/tests/Requests/BudgetTypeTest.php b/tests/Requests/BudgetTypeTest.php index 03d0082..61f3b79 100644 --- a/tests/Requests/BudgetTypeTest.php +++ b/tests/Requests/BudgetTypeTest.php @@ -10,7 +10,24 @@ it('calls the getBudgetTypes method in the BudgetType resource', function () { Saloon::fake([ - GetBudgetTypesRequest::class => MockResponse::fixture('budgetType.getBudgetTypes'), + GetBudgetTypesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetBudgetTypesRequest); diff --git a/tests/Requests/ChangeTest.php b/tests/Requests/ChangeTest.php index 98079fc..66ea2d6 100644 --- a/tests/Requests/ChangeTest.php +++ b/tests/Requests/ChangeTest.php @@ -11,7 +11,15 @@ it('calls the getChange method in the Change resource', function () { Saloon::fake([ - GetChangeRequest::class => MockResponse::fixture('change.getChange'), + GetChangeRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->change()->getChange( @@ -25,7 +33,24 @@ it('calls the getChanges method in the Change resource', function () { Saloon::fake([ - GetChangesRequest::class => MockResponse::fixture('change.getChanges'), + GetChangesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], + ], 200), ]); $request = (new GetChangesRequest); diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index f5b4319..54bf863 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -11,7 +11,7 @@ it('calls the postCorrections method in the Correction resource', function () { Saloon::fake([ - PostCorrectionsRequest::class => MockResponse::fixture('correction.postCorrections'), + PostCorrectionsRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->correction()->postCorrections( @@ -25,7 +25,7 @@ it('calls the patchCorrection method in the Correction resource', function () { Saloon::fake([ - PatchCorrectionRequest::class => MockResponse::fixture('correction.patchCorrection'), + PatchCorrectionRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->correction()->patchCorrection( diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index a1dd088..4d9540f 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -15,7 +15,24 @@ it('calls the getCustomers method in the Customer resource', function () { Saloon::fake([ - GetCustomersRequest::class => MockResponse::fixture('customer.getCustomers'), + GetCustomersRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetCustomersRequest) @@ -39,7 +56,7 @@ it('calls the postCustomers method in the Customer resource', function () { Saloon::fake([ - PostCustomersRequest::class => MockResponse::fixture('customer.postCustomers'), + PostCustomersRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->customer()->postCustomers( @@ -53,7 +70,15 @@ it('calls the getCustomer method in the Customer resource', function () { Saloon::fake([ - GetCustomerRequest::class => MockResponse::fixture('customer.getCustomer'), + GetCustomerRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'data' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->customer()->getCustomer( @@ -67,7 +92,7 @@ it('calls the deleteCustomer method in the Customer resource', function () { Saloon::fake([ - DeleteCustomerRequest::class => MockResponse::fixture('customer.deleteCustomer'), + DeleteCustomerRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->customer()->deleteCustomer( @@ -81,7 +106,7 @@ it('calls the patchCustomer method in the Customer resource', function () { Saloon::fake([ - PatchCustomerRequest::class => MockResponse::fixture('customer.patchCustomer'), + PatchCustomerRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->customer()->patchCustomer( diff --git a/tests/Requests/DailyProgressTest.php b/tests/Requests/DailyProgressTest.php index 79bf9fc..0ac8c78 100644 --- a/tests/Requests/DailyProgressTest.php +++ b/tests/Requests/DailyProgressTest.php @@ -10,7 +10,24 @@ it('calls the getDailyProgresses method in the DailyProgress resource', function () { Saloon::fake([ - GetDailyProgressesRequest::class => MockResponse::fixture('dailyProgress.getDailyProgresses'), + GetDailyProgressesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetDailyProgressesRequest); diff --git a/tests/Requests/EntriesExportTest.php b/tests/Requests/EntriesExportTest.php index 29b5aac..8bdba8e 100644 --- a/tests/Requests/EntriesExportTest.php +++ b/tests/Requests/EntriesExportTest.php @@ -10,7 +10,15 @@ it('calls the getBudgetEntriesExport method in the EntriesExport resource', function () { Saloon::fake([ - GetBudgetEntriesExportRequest::class => MockResponse::fixture('entriesExport.getBudgetEntriesExport'), + GetBudgetEntriesExportRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->entriesExport()->getBudgetEntriesExport( diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index ef2ec5e..f6d9b71 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -13,7 +13,24 @@ it('calls the getEntrySuggestions method in the EntrySuggestion resource', function () { Saloon::fake([ - GetEntrySuggestionsRequest::class => MockResponse::fixture('entrySuggestion.getEntrySuggestions'), + GetEntrySuggestionsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetEntrySuggestionsRequest) @@ -37,7 +54,15 @@ it('calls the getEntrySuggestion method in the EntrySuggestion resource', function () { Saloon::fake([ - GetEntrySuggestionRequest::class => MockResponse::fixture('entrySuggestion.getEntrySuggestion'), + GetEntrySuggestionRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'data' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->entrySuggestion()->getEntrySuggestion( @@ -51,7 +76,7 @@ it('calls the deleteEntrySuggestion method in the EntrySuggestion resource', function () { Saloon::fake([ - DeleteEntrySuggestionRequest::class => MockResponse::fixture('entrySuggestion.deleteEntrySuggestion'), + DeleteEntrySuggestionRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->entrySuggestion()->deleteEntrySuggestion( diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 83a6f86..1519025 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -15,7 +15,24 @@ it('calls the getEntries method in the Entry resource', function () { Saloon::fake([ - GetEntriesRequest::class => MockResponse::fixture('entry.getEntries'), + GetEntriesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetEntriesRequest(include: 'test string')) @@ -43,7 +60,7 @@ it('calls the postEntries method in the Entry resource', function () { Saloon::fake([ - PostEntriesRequest::class => MockResponse::fixture('entry.postEntries'), + PostEntriesRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->entry()->postEntries( @@ -57,7 +74,15 @@ it('calls the getEntry method in the Entry resource', function () { Saloon::fake([ - GetEntryRequest::class => MockResponse::fixture('entry.getEntry'), + GetEntryRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'data' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->entry()->getEntry( @@ -71,7 +96,7 @@ it('calls the deleteEntry method in the Entry resource', function () { Saloon::fake([ - DeleteEntryRequest::class => MockResponse::fixture('entry.deleteEntry'), + DeleteEntryRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->entry()->deleteEntry( @@ -85,7 +110,7 @@ it('calls the patchEntry method in the Entry resource', function () { Saloon::fake([ - PatchEntryRequest::class => MockResponse::fixture('entry.patchEntry'), + PatchEntryRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->entry()->patchEntry( diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index 82c7fd2..611a2eb 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -10,7 +10,7 @@ it('calls the postEvents method in the Event resource', function () { Saloon::fake([ - PostEventsRequest::class => MockResponse::fixture('event.postEvents'), + PostEventsRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->event()->postEvents( diff --git a/tests/Requests/ExportMailTest.php b/tests/Requests/ExportMailTest.php index bebeace..925515f 100644 --- a/tests/Requests/ExportMailTest.php +++ b/tests/Requests/ExportMailTest.php @@ -10,7 +10,24 @@ it('calls the getBudgetsExportMails method in the ExportMail resource', function () { Saloon::fake([ - GetBudgetsExportMailsRequest::class => MockResponse::fixture('exportMail.getBudgetsExportMails'), + GetBudgetsExportMailsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], + ], 200), ]); $request = (new GetBudgetsExportMailsRequest); diff --git a/tests/Requests/IncidentTest.php b/tests/Requests/IncidentTest.php index 37a34aa..ec2cfca 100644 --- a/tests/Requests/IncidentTest.php +++ b/tests/Requests/IncidentTest.php @@ -11,7 +11,15 @@ it('calls the getIncident method in the Incident resource', function () { Saloon::fake([ - GetIncidentRequest::class => MockResponse::fixture('incident.getIncident'), + GetIncidentRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->incident()->getIncident( @@ -25,7 +33,24 @@ it('calls the getIncidents method in the Incident resource', function () { Saloon::fake([ - GetIncidentsRequest::class => MockResponse::fixture('incident.getIncidents'), + GetIncidentsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], + ], 200), ]); $request = (new GetIncidentsRequest); diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index 888d92c..a6c2f1a 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -10,7 +10,7 @@ it('calls the postOvertimeMarkAsExported method in the MarkAsExported resource', function () { Saloon::fake([ - PostOvertimeMarkAsExportedRequest::class => MockResponse::fixture('markAsExported.postOvertimeMarkAsExported'), + PostOvertimeMarkAsExportedRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported( diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index b930999..79c8087 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -10,7 +10,7 @@ it('calls the postEntryMarkAsInvoiced method in the MarkAsInvoiced resource', function () { Saloon::fake([ - PostEntryMarkAsInvoicedRequest::class => MockResponse::fixture('markAsInvoiced.postEntryMarkAsInvoiced'), + PostEntryMarkAsInvoicedRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced( diff --git a/tests/Requests/MeTest.php b/tests/Requests/MeTest.php index 4896170..0ded68f 100644 --- a/tests/Requests/MeTest.php +++ b/tests/Requests/MeTest.php @@ -10,7 +10,24 @@ it('calls the getMes method in the Me resource', function () { Saloon::fake([ - GetMesRequest::class => MockResponse::fixture('me.getMes'), + GetMesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], + ], 200), ]); $request = (new GetMesRequest); diff --git a/tests/Requests/NumberTest.php b/tests/Requests/NumberTest.php index 1d2dab5..96e6534 100644 --- a/tests/Requests/NumberTest.php +++ b/tests/Requests/NumberTest.php @@ -10,7 +10,15 @@ it('calls the getIncidentsNumber method in the Number resource', function () { Saloon::fake([ - GetIncidentsNumberRequest::class => MockResponse::fixture('number.getIncidentsNumber'), + GetIncidentsNumberRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->number()->getIncidentsNumber( diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index d3574ab..6b670de 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -11,7 +11,24 @@ it('calls the getOvertimes method in the Overtime resource', function () { Saloon::fake([ - GetOvertimesRequest::class => MockResponse::fixture('overtime.getOvertimes'), + GetOvertimesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetOvertimesRequest) diff --git a/tests/Requests/PeriodTest.php b/tests/Requests/PeriodTest.php index 9643da6..f69f057 100644 --- a/tests/Requests/PeriodTest.php +++ b/tests/Requests/PeriodTest.php @@ -10,7 +10,15 @@ it('calls the getBudgetPeriods method in the Period resource', function () { Saloon::fake([ - GetBudgetPeriodsRequest::class => MockResponse::fixture('period.getBudgetPeriods'), + GetBudgetPeriodsRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->period()->getBudgetPeriods( diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index 784c54c..f85ccfc 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -14,7 +14,24 @@ it('calls the getTeams method in the Team resource', function () { Saloon::fake([ - GetTeamsRequest::class => MockResponse::fixture('team.getTeams'), + GetTeamsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetTeamsRequest); @@ -28,7 +45,7 @@ it('calls the postTeams method in the Team resource', function () { Saloon::fake([ - PostTeamsRequest::class => MockResponse::fixture('team.postTeams'), + PostTeamsRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->team()->postTeams( @@ -42,7 +59,15 @@ it('calls the getTeam method in the Team resource', function () { Saloon::fake([ - GetTeamRequest::class => MockResponse::fixture('team.getTeam'), + GetTeamRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'data' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->team()->getTeam( @@ -56,7 +81,7 @@ it('calls the deleteTeam method in the Team resource', function () { Saloon::fake([ - DeleteTeamRequest::class => MockResponse::fixture('team.deleteTeam'), + DeleteTeamRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->team()->deleteTeam( @@ -70,7 +95,7 @@ it('calls the patchTeam method in the Team resource', function () { Saloon::fake([ - PatchTeamRequest::class => MockResponse::fixture('team.patchTeam'), + PatchTeamRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->team()->patchTeam( diff --git a/tests/Requests/TimeSpentTotalTest.php b/tests/Requests/TimeSpentTotalTest.php index 05ac368..7464411 100644 --- a/tests/Requests/TimeSpentTotalTest.php +++ b/tests/Requests/TimeSpentTotalTest.php @@ -11,7 +11,24 @@ it('calls the getTimeSpentTotals method in the TimeSpentTotal resource', function () { Saloon::fake([ - GetTimeSpentTotalsRequest::class => MockResponse::fixture('timeSpentTotal.getTimeSpentTotals'), + GetTimeSpentTotalsRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetTimeSpentTotalsRequest) diff --git a/tests/Requests/UserCustomerHoursAggregateTest.php b/tests/Requests/UserCustomerHoursAggregateTest.php index 1f244a7..ec6eafb 100644 --- a/tests/Requests/UserCustomerHoursAggregateTest.php +++ b/tests/Requests/UserCustomerHoursAggregateTest.php @@ -11,7 +11,24 @@ it('calls the getUserCustomerHoursAggregates method in the UserCustomerHoursAggregate resource', function () { Saloon::fake([ - GetUserCustomerHoursAggregatesRequest::class => MockResponse::fixture('userCustomerHoursAggregate.getUserCustomerHoursAggregates'), + GetUserCustomerHoursAggregatesRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'data' => [], + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'data' => [], + ], + ], + ], + ], 200), ]); $request = (new GetUserCustomerHoursAggregatesRequest) diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index ae2e174..0e84652 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -15,7 +15,24 @@ it('calls the getUsers method in the User resource', function () { Saloon::fake([ - GetUsersRequest::class => MockResponse::fixture('user.getUsers'), + GetUsersRequest::class => MockResponse::make([ + 'data' => [ + 0 => [ + 'type' => 'resources', + 'id' => 'mock-id-1', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + 1 => [ + 'type' => 'resources', + 'id' => 'mock-id-2', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], + ], 200), ]); $request = (new GetUsersRequest) @@ -39,7 +56,7 @@ it('calls the postUsers method in the User resource', function () { Saloon::fake([ - PostUsersRequest::class => MockResponse::fixture('user.postUsers'), + PostUsersRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->user()->postUsers( @@ -53,7 +70,15 @@ it('calls the getUser method in the User resource', function () { Saloon::fake([ - GetUserRequest::class => MockResponse::fixture('user.getUser'), + GetUserRequest::class => MockResponse::make([ + 'data' => [ + 'type' => 'resources', + 'id' => 'mock-id-123', + 'attributes' => [ + 'name' => 'Mock value', + ], + ], + ], 200), ]); $response = $this->timaticConnector->user()->getUser( @@ -67,7 +92,7 @@ it('calls the deleteUser method in the User resource', function () { Saloon::fake([ - DeleteUserRequest::class => MockResponse::fixture('user.deleteUser'), + DeleteUserRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->user()->deleteUser( @@ -81,7 +106,7 @@ it('calls the patchUser method in the User resource', function () { Saloon::fake([ - PatchUserRequest::class => MockResponse::fixture('user.patchUser'), + PatchUserRequest::class => MockResponse::make([], 200), ]); $response = $this->timaticConnector->user()->patchUser( From 5a12291bbf96032fd07f09474764997a8ec44265 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 21:17:19 +0100 Subject: [PATCH 28/62] add request body validation for mutation requests --- claude-todo.md | 40 +- generator/JsonApiPestTestGenerator.php | 18 +- generator/MutationRequestTestGenerator.php | 435 +++++++++++++++++++ generator/stubs/pest-mutation-test-func.stub | 13 + tests/Requests/ApproveTest.php | 23 +- tests/Requests/BudgetTest.php | 55 ++- tests/Requests/CorrectionTest.php | 48 +- tests/Requests/CustomerTest.php | 55 ++- tests/Requests/EntryTest.php | 55 ++- tests/Requests/EventTest.php | 29 +- tests/Requests/MarkAsExportedTest.php | 23 +- tests/Requests/MarkAsInvoicedTest.php | 23 +- tests/Requests/TeamTest.php | 56 ++- tests/Requests/UserTest.php | 47 +- 14 files changed, 805 insertions(+), 115 deletions(-) create mode 100644 generator/MutationRequestTestGenerator.php create mode 100644 generator/stubs/pest-mutation-test-func.stub diff --git a/claude-todo.md b/claude-todo.md index 12dad2b..51a4176 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -1,28 +1,10 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. -## Task 1: Use Fluent Filters in GET Collection Tests +## Task 1 - Extract -**Goal:** Replace 80+ filter constructor parameters with fluent filter chain - -**Changes:** -- Modify `JsonApiPestTestGenerator.php` to detect GET collection requests (Paginatable interface) -- Add `generateFilterChain()` method - creates 2-3 representative filter examples -- Update test stub to use: `$request = (new GetEntriesRequest())->filter('userId', - 'test-123')->filter('startedAt', '2025-01-01', Operator::GreaterThanOrEquals)` -- Add proper imports (Operator enum) -- Regenerate with `composer regenerate` and test with `composer test` - -## Task 2: Replace Fixtures with Inline Mock Data - -**Goal:** Remove fixture dependencies, use inline `MockResponse::make()` - -**Changes:** -- Add `generateMockResponseBody()` method - creates JSON:API structure from endpoint schema -- Generate realistic test data based on property types -- Use `MockResponse::make(body: ['data' => [...]])` instead of `MockResponse::fixture()` -- Handle both single resource and collection responses -- Regenerate with `composer regenerate` and test with `composer test` +- Can we extract Singular GET Test logic from JsonAPIPestGenerator and Delete test logic into their own generator +classes? Perhaps all four classes can share some logic using traits ## Task 3: Validate DTO Hydration in GET Tests @@ -34,21 +16,11 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid - Check 2-3 key DTO properties are present with expected values - Regenerate with `composer regenerate` and test with `composer test` -## Task 4: Validate JSON:API Request Bodies in POST/PATCH Tests - -**Goal:** Verify DTOs serialize to proper JSON:API format - -**Changes:** -- Create DTO instance with test data in test -- Pass DTO to POST/PATCH method -- Use `Saloon::assertSent(function ($request) { ... })` callback -- Validate `data.type`, `data.attributes` structure -- Check specific attribute values match DTO input -- Regenerate with `composer regenerate` and test with `composer test` - - ## TASK: replace "type: resource" with actual resource in mocked GET data ## TASK 5: Add params for filters in Resources +- Make a plan how we can add filters like $timaticConnector->budget->getBudgets(['key'=>'value']); +- or come up with a better way to add filters to resources + **Note:** Each task is independent and can be completed, tested, and committed separately. \ No newline at end of file diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index bc176c9..cf61430 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -13,11 +13,14 @@ class JsonApiPestTestGenerator extends PestTestGenerator { protected CollectionRequestTestGenerator $collectionTestGenerator; + protected MutationRequestTestGenerator $mutationTestGenerator; + protected ?array $openApiSpec = null; public function __construct() { $this->collectionTestGenerator = new CollectionRequestTestGenerator; + $this->mutationTestGenerator = new MutationRequestTestGenerator; } /** @@ -79,6 +82,11 @@ protected function getTestFunctionStubPath(Endpoint $endpoint): string return $this->collectionTestGenerator->getStubPath(); } + // Delegate to MutationRequestTestGenerator for mutation requests + if ($this->mutationTestGenerator->isMutationRequest($endpoint)) { + return $this->mutationTestGenerator->getStubPath(); + } + return __DIR__.'/stubs/pest-resource-test-func.stub'; } @@ -143,7 +151,15 @@ protected function replaceAdditionalStubVariables( $functionStub = $this->collectionTestGenerator->replaceStubVariables($functionStub, $endpoint); } - // Only generate mock data for GET requests + // Delegate to MutationRequestTestGenerator for mutation requests + if ($this->mutationTestGenerator->isMutationRequest($endpoint)) { + $functionStub = $this->mutationTestGenerator->replaceStubVariables($functionStub, $endpoint); + + // Return early - mutation generator handles everything + return $functionStub; + } + + // Generate mock data for GET requests (without validation) if ($endpoint->method->isGet()) { // Replace fixture with inline mock data // Match both the template variable and actual fixture names diff --git a/generator/MutationRequestTestGenerator.php b/generator/MutationRequestTestGenerator.php new file mode 100644 index 0000000..144a12f --- /dev/null +++ b/generator/MutationRequestTestGenerator.php @@ -0,0 +1,435 @@ +method->isPost() || $endpoint->method->isPatch(); + } + + /** + * Get the stub path for mutation request tests + */ + public function getStubPath(): string + { + return __DIR__.'/stubs/pest-mutation-test-func.stub'; + } + + /** + * Replace stub variables with mutation-specific content + */ + public function replaceStubVariables(string $functionStub, Endpoint $endpoint): string + { + // Generate DTO instantiation code + $dtoInstantiation = $this->generateDtoInstantiation($endpoint); + $functionStub = str_replace('{{ dtoInstantiation }}', $dtoInstantiation, $functionStub); + + // Generate body validation code + $bodyValidation = $this->generateBodyValidation($endpoint); + $functionStub = str_replace('{{ bodyValidation }}', $bodyValidation, $functionStub); + + // Generate method arguments (including $dto) + $methodArguments = $this->generateMethodArguments($endpoint); + $functionStub = str_replace('{{ mutationMethodArguments }}', $methodArguments, $functionStub); + + return $functionStub; + } + + /** + * Generate method arguments for resource method call + */ + protected function generateMethodArguments(Endpoint $endpoint): string + { + $args = []; + + // Add path parameters first (e.g., budgetId for PATCH) + foreach ($endpoint->pathParameters as $param) { + $paramName = NameHelper::safeVariableName($param->name); + // Add 'Id' suffix if not already present + if (! str_ends_with($paramName, 'Id')) { + $paramName .= 'Id'; + } + $args[] = "{$paramName}: 'test string'"; + } + + // Add $dto parameter last + $args[] = '$dto'; + + return implode(', ', $args); + } + + /** + * Generate DTO instantiation code with sample data + */ + protected function generateDtoInstantiation(Endpoint $endpoint): string + { + $dtoClassName = $this->getDtoClassName($endpoint); + $properties = $this->generateDtoProperties($endpoint); + + $lines = []; + $lines[] = " \$dto = new \\Timatic\\SDK\\Dto\\{$dtoClassName};"; + $lines[] = $properties; + $lines[] = ' // todo: add every other DTO field'; + + return implode("\n", $lines); + } + + /** + * Generate DTO property assignments + */ + protected function generateDtoProperties(Endpoint $endpoint): string + { + $schema = $this->getRequestSchemaForEndpoint($endpoint); + if (! $schema) { + return " \$dto->name = 'test value';"; + } + + $properties = $this->extractPropertiesFromSchema($schema); + $lines = []; + + // Limit to first 4 properties for the test + $propertiesToShow = array_slice($properties, 0, 4); + + foreach ($propertiesToShow as $propName => $propSpec) { + $value = $this->generateTestValueForProperty($propName, $propSpec); + $lines[] = " \$dto->{$propName} = {$value};"; + } + + return implode("\n", $lines); + } + + /** + * Generate body validation code + */ + protected function generateBodyValidation(Endpoint $endpoint): string + { + $resourceType = $this->getResourceTypeFromEndpoint($endpoint); + $schema = $this->getRequestSchemaForEndpoint($endpoint); + + if (! $schema) { + return $this->generateFallbackBodyValidation($resourceType, $endpoint); + } + + $properties = $this->extractPropertiesFromSchema($schema); + $lines = []; + + $lines[] = ' $mockClient->assertSent(function (Request $request) {'; + $lines[] = ' expect($request->body()->all())'; + $lines[] = " ->toHaveKey('data')"; + + // POST calls don't have an ID field in the request + if ($endpoint->method->isPost()) { + $lines[] = ' // POST calls dont have an ID field'; + } + + $lines[] = " ->data->type->toBe('{$resourceType}')"; + + // Generate attribute validations + $attributeValidations = $this->generateAttributeValidations($properties); + if ($attributeValidations) { + $lines[] = ' ->data->attributes->scoped(fn ($attributes) => $attributes'; + $lines[] = $attributeValidations; + $lines[] = ' );'; + } + + $lines[] = ''; + $lines[] = ' return true;'; + $lines[] = ' });'; + + return implode("\n", $lines); + } + + /** + * Generate attribute validation chain + */ + protected function generateAttributeValidations(array $properties): string + { + $lines = []; + + // Limit to first 4 properties for the test + $propertiesToShow = array_slice($properties, 0, 4); + + foreach ($propertiesToShow as $propName => $propSpec) { + $value = $this->generateTestValueForProperty($propName, $propSpec); + $assertionValue = $this->formatValueForAssertion($value); + $lines[] = " ->{$propName}->toBe({$assertionValue})"; + } + + return implode("\n", $lines); + } + + /** + * Get the DTO class name for an endpoint + */ + protected function getDtoClassName(Endpoint $endpoint): string + { + // Try to extract from endpoint collection name + if ($endpoint->collection) { + $resourceName = NameHelper::resourceClassName($endpoint->collection); + + // Remove trailing 's' for singular DTO name + return rtrim($resourceName, 's'); + } + + // Fallback: try to parse from endpoint name + $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); + // Remove method prefix (post, patch) + $name = preg_replace('/^(post|patch)/i', '', $name); + // Remove trailing 's' for singular + $name = rtrim($name, 's'); + + return NameHelper::resourceClassName($name); + } + + /** + * Get the resource type for JSON:API (plural, lowercase) + */ + protected function getResourceTypeFromEndpoint(Endpoint $endpoint): string + { + if ($endpoint->collection) { + return NameHelper::safeVariableName($endpoint->collection); + } + + // Fallback: parse from endpoint path + $path = $endpoint->path; + // Extract first path segment (e.g., /budgets -> budgets) + preg_match('#^/([^/]+)#', $path, $matches); + + return $matches[1] ?? 'resources'; + } + + /** + * Load and cache the OpenAPI specification + */ + protected function getOpenApiSpec(): array + { + if ($this->openApiSpec === null) { + $specPath = __DIR__.'/../openapi.json'; + if (file_exists($specPath)) { + $this->openApiSpec = json_decode(file_get_contents($specPath), true); + } else { + $this->openApiSpec = []; + } + } + + return $this->openApiSpec; + } + + /** + * Get the request body schema for an endpoint from the OpenAPI spec + */ + protected function getRequestSchemaForEndpoint(Endpoint $endpoint): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec)) { + return null; + } + + // Find the endpoint spec by operationId + $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); + if (! $endpointSpec || ! isset($endpointSpec['requestBody']['content']['application/json']['schema'])) { + return null; + } + + $schema = $endpointSpec['requestBody']['content']['application/json']['schema']; + + // Resolve $ref if present + if (isset($schema['$ref'])) { + $schema = $this->resolveSchemaReference($schema['$ref']); + } + + return $schema; + } + + /** + * Find an endpoint specification by operation ID + */ + protected function findEndpointSpecByOperationId(string $operationId): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec) || ! isset($spec['paths'])) { + return null; + } + + // Search through all paths and methods to find the matching operationId + foreach ($spec['paths'] as $path => $pathItem) { + foreach ($pathItem as $method => $operation) { + // Skip non-operation keys (like parameters, summary, etc.) + if (! is_array($operation) || ! isset($operation['operationId'])) { + continue; + } + + if ($operation['operationId'] === $operationId) { + return $operation; + } + } + } + + return null; + } + + /** + * Resolve a schema reference ($ref) + */ + protected function resolveSchemaReference(string $ref): ?array + { + $spec = $this->getOpenApiSpec(); + $refPath = str_replace('#/', '', $ref); + $refParts = explode('/', $refPath); + + $referencedSchema = $spec; + foreach ($refParts as $part) { + if (! isset($referencedSchema[$part])) { + return null; + } + $referencedSchema = $referencedSchema[$part]; + } + + return $referencedSchema; + } + + /** + * Extract properties from a schema (handling JSON:API structure) + */ + protected function extractPropertiesFromSchema(array $schema): array + { + // For JSON:API, properties are nested in data.attributes + if (isset($schema['properties']['data']['properties']['attributes']['properties'])) { + return $schema['properties']['data']['properties']['attributes']['properties']; + } + + // Fallback: check if properties has attributes + if (isset($schema['properties']['attributes']['properties'])) { + return $schema['properties']['attributes']['properties']; + } + + // Direct properties + if (isset($schema['properties'])) { + $properties = $schema['properties']; + // Remove non-attribute fields + unset($properties['id'], $properties['type'], $properties['attributes'], $properties['relationships']); + + return $properties; + } + + return []; + } + + /** + * Generate appropriate test value based on property name and spec + */ + protected function generateTestValueForProperty(string $property, array $spec = []): string + { + // Check for example in spec + if (isset($spec['example'])) { + $value = $spec['example']; + if (is_string($value)) { + return "'{$value}'"; + } + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + if (is_numeric($value)) { + return (string) $value; + } + } + + $type = $spec['type'] ?? 'string'; + $format = $spec['format'] ?? null; + + // DateTime fields + if ($format === 'date-time' || str_contains($property, 'At') || str_contains($property, 'Date')) { + return "'2025-01-01T10:00:00Z'"; + } + + // ID properties + if (str_ends_with($property, 'Id')) { + return "'test-id-123'"; + } + + // Boolean properties + if ($type === 'boolean' || str_starts_with($property, 'is') || str_starts_with($property, 'has')) { + return 'false'; + } + + // Numeric properties + if ($type === 'integer' || $type === 'number') { + return '123'; + } + + // String properties with common names + if ($property === 'title') { + return "'test title'"; + } + if ($property === 'description') { + return "'test description'"; + } + if ($property === 'name') { + return "'test name'"; + } + + // Default to string + return "'test value'"; + } + + /** + * Format a value for use in assertions + */ + protected function formatValueForAssertion(string $value): string + { + // If value is already quoted, return as-is + if (str_starts_with($value, "'") && str_ends_with($value, "'")) { + return $value; + } + + // Boolean values + if ($value === 'true' || $value === 'false') { + return $value; + } + + // Numeric values + if (is_numeric($value)) { + return $value; + } + + // Wrap in quotes + return "'{$value}'"; + } + + /** + * Generate fallback body validation when schema is not available + */ + protected function generateFallbackBodyValidation(string $resourceType, Endpoint $endpoint): string + { + $lines = []; + $lines[] = ' $mockClient->assertSent(function (Request $request) {'; + $lines[] = ' expect($request->body()->all())'; + $lines[] = " ->toHaveKey('data')"; + + if ($endpoint->method->isPost()) { + $lines[] = ' // POST calls dont have an ID field'; + } + + $lines[] = " ->data->type->toBe('{$resourceType}')"; + $lines[] = ' ->data->attributes->scoped(fn ($attributes) => $attributes'; + $lines[] = " ->name->toBe('test value')"; + $lines[] = ' );'; + $lines[] = ''; + $lines[] = ' return true;'; + $lines[] = ' });'; + + return implode("\n", $lines); + } +} diff --git a/generator/stubs/pest-mutation-test-func.stub b/generator/stubs/pest-mutation-test-func.stub new file mode 100644 index 0000000..dfb3b58 --- /dev/null +++ b/generator/stubs/pest-mutation-test-func.stub @@ -0,0 +1,13 @@ +it('{{ testDescription }}', function () { + $mockClient = Saloon::fake([ + {{ requestClass }}::class => MockResponse::make([], 200), + ]); + + // Create DTO with sample data +{{ dtoInstantiation }} + + $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ mutationMethodArguments }}); + Saloon::assertSent({{ requestClass }}::class); + +{{ bodyValidation }} +}); diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index 80f82d0..62cb49c 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -1,6 +1,7 @@ MockResponse::make([], 200), ]); - $response = $this->timaticConnector->approve()->postOvertimeApprove( - overtimeId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Approve; + $dto->name = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', $dto); Saloon::assertSent(PostOvertimeApproveRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('approve') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->name->toBe('test value') + ); + + return true; + }); }); diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 2d5c8a5..20fa2ef 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -59,17 +59,35 @@ }); it('calls the postBudgets method in the Budget resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PostBudgetsRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->budget()->postBudgets( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Budget; + $dto->budgetTypeId = 'test-id-123'; + $dto->customerId = 'test-id-123'; + $dto->showToCustomer = false; + $dto->changeId = 'test-id-123'; + // todo: add every other DTO field + $this->timaticConnector->budget()->postBudgets($dto); Saloon::assertSent(PostBudgetsRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('budget') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->budgetTypeId->toBe('test-id-123') + ->customerId->toBe('test-id-123') + ->showToCustomer->toBe(false) + ->changeId->toBe('test-id-123') + ); + + return true; + }); }); it('calls the getBudget method in the Budget resource', function () { @@ -109,15 +127,32 @@ }); it('calls the patchBudget method in the Budget resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PatchBudgetRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->budget()->patchBudget( - budgetId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Budget; + $dto->budgetTypeId = 'test-id-123'; + $dto->customerId = 'test-id-123'; + $dto->showToCustomer = false; + $dto->changeId = 'test-id-123'; + // todo: add every other DTO field + $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', $dto); Saloon::assertSent(PatchBudgetRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + ->data->type->toBe('budget') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->budgetTypeId->toBe('test-id-123') + ->customerId->toBe('test-id-123') + ->showToCustomer->toBe(false) + ->changeId->toBe('test-id-123') + ); + + return true; + }); }); diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index 54bf863..76677ed 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -1,6 +1,7 @@ MockResponse::make([], 200), ]); - $response = $this->timaticConnector->correction()->postCorrections( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Correction; + $dto->createdAt = '2025-01-01T10:00:00Z'; + $dto->updatedAt = '2025-01-01T10:00:00Z'; + // todo: add every other DTO field + $this->timaticConnector->correction()->postCorrections($dto); Saloon::assertSent(PostCorrectionsRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('correction') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->createdAt->toBe('2025-01-01T10:00:00Z') + ->updatedAt->toBe('2025-01-01T10:00:00Z') + ); + + return true; + }); }); it('calls the patchCorrection method in the Correction resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PatchCorrectionRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->correction()->patchCorrection( - correctionId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Correction; + $dto->createdAt = '2025-01-01T10:00:00Z'; + $dto->updatedAt = '2025-01-01T10:00:00Z'; + // todo: add every other DTO field + $this->timaticConnector->correction()->patchCorrection(correctionId: 'test string', $dto); Saloon::assertSent(PatchCorrectionRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + ->data->type->toBe('correction') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->createdAt->toBe('2025-01-01T10:00:00Z') + ->updatedAt->toBe('2025-01-01T10:00:00Z') + ); + + return true; + }); }); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index 4d9540f..8c61649 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -55,17 +55,35 @@ }); it('calls the postCustomers method in the Customer resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PostCustomersRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->customer()->postCustomers( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Customer; + $dto->externalId = 'test-id-123'; + $dto->name = 'test name'; + $dto->hourlyRate = 'test value'; + $dto->accountManagerUserId = 'test-id-123'; + // todo: add every other DTO field + $this->timaticConnector->customer()->postCustomers($dto); Saloon::assertSent(PostCustomersRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('customer') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->externalId->toBe('test-id-123') + ->name->toBe('test name') + ->hourlyRate->toBe('test value') + ->accountManagerUserId->toBe('test-id-123') + ); + + return true; + }); }); it('calls the getCustomer method in the Customer resource', function () { @@ -105,15 +123,32 @@ }); it('calls the patchCustomer method in the Customer resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PatchCustomerRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->customer()->patchCustomer( - customerId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Customer; + $dto->externalId = 'test-id-123'; + $dto->name = 'test name'; + $dto->hourlyRate = 'test value'; + $dto->accountManagerUserId = 'test-id-123'; + // todo: add every other DTO field + $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', $dto); Saloon::assertSent(PatchCustomerRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + ->data->type->toBe('customer') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->externalId->toBe('test-id-123') + ->name->toBe('test name') + ->hourlyRate->toBe('test value') + ->accountManagerUserId->toBe('test-id-123') + ); + + return true; + }); }); diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 1519025..77fed9b 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -59,17 +59,35 @@ }); it('calls the postEntries method in the Entry resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PostEntriesRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->entry()->postEntries( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Entry; + $dto->ticketId = 'test-id-123'; + $dto->ticketNumber = 'test value'; + $dto->ticketTitle = 'test value'; + $dto->ticketType = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->entry()->postEntries($dto); Saloon::assertSent(PostEntriesRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('entry') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->ticketId->toBe('test-id-123') + ->ticketNumber->toBe('test value') + ->ticketTitle->toBe('test value') + ->ticketType->toBe('test value') + ); + + return true; + }); }); it('calls the getEntry method in the Entry resource', function () { @@ -109,15 +127,32 @@ }); it('calls the patchEntry method in the Entry resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PatchEntryRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->entry()->patchEntry( - entryId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Entry; + $dto->ticketId = 'test-id-123'; + $dto->ticketNumber = 'test value'; + $dto->ticketTitle = 'test value'; + $dto->ticketType = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->entry()->patchEntry(entryId: 'test string', $dto); Saloon::assertSent(PatchEntryRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + ->data->type->toBe('entry') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->ticketId->toBe('test-id-123') + ->ticketNumber->toBe('test value') + ->ticketTitle->toBe('test value') + ->ticketType->toBe('test value') + ); + + return true; + }); }); diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index 611a2eb..68f82c8 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -1,6 +1,7 @@ MockResponse::make([], 200), ]); - $response = $this->timaticConnector->event()->postEvents( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Event; + $dto->userId = 'test-id-123'; + $dto->budgetId = 'test-id-123'; + $dto->ticketId = 'test-id-123'; + $dto->sourceId = 'test-id-123'; + // todo: add every other DTO field + $this->timaticConnector->event()->postEvents($dto); Saloon::assertSent(PostEventsRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('event') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->userId->toBe('test-id-123') + ->budgetId->toBe('test-id-123') + ->ticketId->toBe('test-id-123') + ->sourceId->toBe('test-id-123') + ); + + return true; + }); }); diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index a6c2f1a..f29f094 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -1,6 +1,7 @@ MockResponse::make([], 200), ]); - $response = $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported( - overtimeId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\MarkAsExported; + $dto->name = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', $dto); Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('markAsExported') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->name->toBe('test value') + ); + + return true; + }); }); diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index 79c8087..d47a298 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -1,6 +1,7 @@ MockResponse::make([], 200), ]); - $response = $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced( - entryId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\MarkAsInvoiced; + $dto->name = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced(entryId: 'test string', $dto); Saloon::assertSent(PostEntryMarkAsInvoicedRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('markAsInvoiced') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->name->toBe('test value') + ); + + return true; + }); }); diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index f85ccfc..a43e95b 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -1,6 +1,7 @@ MockResponse::make([], 200), ]); - $response = $this->timaticConnector->team()->postTeams( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Team; + $dto->externalId = 'test-id-123'; + $dto->name = 'test name'; + $dto->createdAt = '2025-01-01T10:00:00Z'; + $dto->updatedAt = '2025-01-01T10:00:00Z'; + // todo: add every other DTO field + $this->timaticConnector->team()->postTeams($dto); Saloon::assertSent(PostTeamsRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('team') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->externalId->toBe('test-id-123') + ->name->toBe('test name') + ->createdAt->toBe('2025-01-01T10:00:00Z') + ->updatedAt->toBe('2025-01-01T10:00:00Z') + ); + + return true; + }); }); it('calls the getTeam method in the Team resource', function () { @@ -94,15 +113,32 @@ }); it('calls the patchTeam method in the Team resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PatchTeamRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->team()->patchTeam( - teamId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\Team; + $dto->externalId = 'test-id-123'; + $dto->name = 'test name'; + $dto->createdAt = '2025-01-01T10:00:00Z'; + $dto->updatedAt = '2025-01-01T10:00:00Z'; + // todo: add every other DTO field + $this->timaticConnector->team()->patchTeam(teamId: 'test string', $dto); Saloon::assertSent(PatchTeamRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + ->data->type->toBe('team') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->externalId->toBe('test-id-123') + ->name->toBe('test name') + ->createdAt->toBe('2025-01-01T10:00:00Z') + ->updatedAt->toBe('2025-01-01T10:00:00Z') + ); + + return true; + }); }); diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index 0e84652..d625b14 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -55,17 +55,31 @@ }); it('calls the postUsers method in the User resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PostUsersRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->user()->postUsers( - - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\User; + $dto->externalId = 'test-id-123'; + $dto->email = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->user()->postUsers($dto); Saloon::assertSent(PostUsersRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + // POST calls dont have an ID field + ->data->type->toBe('user') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->externalId->toBe('test-id-123') + ->email->toBe('test value') + ); + + return true; + }); }); it('calls the getUser method in the User resource', function () { @@ -105,15 +119,28 @@ }); it('calls the patchUser method in the User resource', function () { - Saloon::fake([ + $mockClient = Saloon::fake([ PatchUserRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->user()->patchUser( - userId: 'test string' - ); + // Create DTO with sample data + $dto = new \Timatic\SDK\Dto\User; + $dto->externalId = 'test-id-123'; + $dto->email = 'test value'; + // todo: add every other DTO field + $this->timaticConnector->user()->patchUser(userId: 'test string', $dto); Saloon::assertSent(PatchUserRequest::class); - expect($response->status())->toBe(200); + $mockClient->assertSent(function (Request $request) { + expect($request->body()->all()) + ->toHaveKey('data') + ->data->type->toBe('user') + ->data->attributes->scoped(fn ($attributes) => $attributes + ->externalId->toBe('test-id-123') + ->email->toBe('test value') + ); + + return true; + }); }); From 70cd2ca82e34a93e3e4e9a921cd3e8af96085a19 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Fri, 21 Nov 2025 22:25:49 +0100 Subject: [PATCH 29/62] move generators to separate directory --- generator/JsonApiPestTestGenerator.php | 2 ++ .../{ => TestGenerators}/CollectionRequestTestGenerator.php | 2 +- generator/{ => TestGenerators}/MutationRequestTestGenerator.php | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) rename generator/{ => TestGenerators}/CollectionRequestTestGenerator.php (99%) rename generator/{ => TestGenerators}/MutationRequestTestGenerator.php (99%) diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index cf61430..e6d8f5d 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -8,6 +8,8 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\PestTestGenerator; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Timatic\SDK\Generator\TestGenerators\CollectionRequestTestGenerator; +use Timatic\SDK\Generator\TestGenerators\MutationRequestTestGenerator; class JsonApiPestTestGenerator extends PestTestGenerator { diff --git a/generator/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php similarity index 99% rename from generator/CollectionRequestTestGenerator.php rename to generator/TestGenerators/CollectionRequestTestGenerator.php index 20920a9..d98cb7d 100644 --- a/generator/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Generator; +namespace Timatic\SDK\Generator\TestGenerators; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; diff --git a/generator/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php similarity index 99% rename from generator/MutationRequestTestGenerator.php rename to generator/TestGenerators/MutationRequestTestGenerator.php index 144a12f..4d88b06 100644 --- a/generator/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Generator; +namespace Timatic\SDK\Generator\TestGenerators; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; From 29729bf75fb43b117254e2140ea7b55a02bddac4 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 01:21:21 +0100 Subject: [PATCH 30/62] test generator code clean up --- generator/JsonApiPestTestGenerator.php | 349 ++---------------- .../CollectionRequestTestGenerator.php | 83 +++-- .../DeleteRequestTestGenerator.php | 35 ++ .../MutationRequestTestGenerator.php | 222 +---------- .../SingularGetRequestTestGenerator.php | 86 +++++ .../Traits/MockDataGeneratorTrait.php | 183 +++++++++ .../Traits/OpenApiSpecLoaderTrait.php | 74 ++++ .../Traits/SchemaExtractorTrait.php | 131 +++++++ .../Traits/TestValueGeneratorTrait.php | 89 +++++ .../stubs/pest-delete-request-test-func.stub} | 2 +- .../stubs/pest-filter-assertion-block.stub | 0 .../stubs/pest-fluent-filter-test-func.stub | 0 .../stubs/pest-jsonapi-test-func.stub | 0 .../stubs/pest-mutation-test-func.stub | 0 .../stubs/pest-resource-test.stub | 0 .../pest-singular-get-request-test-func.stub | 11 + phpunit.xml | 14 + tests/Requests/BudgetTest.php | 4 +- tests/Requests/EntrySuggestionTest.php | 4 +- tests/Requests/EntryTest.php | 4 +- tests/Requests/OvertimeTest.php | 12 +- .../UserCustomerHoursAggregateTest.php | 8 +- 22 files changed, 740 insertions(+), 571 deletions(-) create mode 100644 generator/TestGenerators/DeleteRequestTestGenerator.php create mode 100644 generator/TestGenerators/SingularGetRequestTestGenerator.php create mode 100644 generator/TestGenerators/Traits/MockDataGeneratorTrait.php create mode 100644 generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php create mode 100644 generator/TestGenerators/Traits/SchemaExtractorTrait.php create mode 100644 generator/TestGenerators/Traits/TestValueGeneratorTrait.php rename generator/{stubs/pest-resource-test-func.stub => TestGenerators/stubs/pest-delete-request-test-func.stub} (77%) rename generator/{ => TestGenerators}/stubs/pest-filter-assertion-block.stub (100%) rename generator/{ => TestGenerators}/stubs/pest-fluent-filter-test-func.stub (100%) rename generator/{ => TestGenerators}/stubs/pest-jsonapi-test-func.stub (100%) rename generator/{ => TestGenerators}/stubs/pest-mutation-test-func.stub (100%) rename generator/{ => TestGenerators}/stubs/pest-resource-test.stub (100%) create mode 100644 generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub create mode 100644 phpunit.xml diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index e6d8f5d..d65723c 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -9,37 +9,33 @@ use Crescat\SaloonSdkGenerator\Generators\PestTestGenerator; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\CollectionRequestTestGenerator; +use Timatic\SDK\Generator\TestGenerators\DeleteRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\MutationRequestTestGenerator; +use Timatic\SDK\Generator\TestGenerators\SingularGetRequestTestGenerator; +use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; class JsonApiPestTestGenerator extends PestTestGenerator { + use MockDataGeneratorTrait; + use OpenApiSpecLoaderTrait; + use SchemaExtractorTrait; + protected CollectionRequestTestGenerator $collectionTestGenerator; protected MutationRequestTestGenerator $mutationTestGenerator; - protected ?array $openApiSpec = null; + protected SingularGetRequestTestGenerator $singularGetTestGenerator; + + protected DeleteRequestTestGenerator $deleteTestGenerator; public function __construct() { $this->collectionTestGenerator = new CollectionRequestTestGenerator; $this->mutationTestGenerator = new MutationRequestTestGenerator; - } - - /** - * Load and cache the OpenAPI specification - */ - protected function getOpenApiSpec(): array - { - if ($this->openApiSpec === null) { - $specPath = __DIR__.'/../openapi.json'; - if (file_exists($specPath)) { - $this->openApiSpec = json_decode(file_get_contents($specPath), true); - } else { - $this->openApiSpec = []; - } - } - - return $this->openApiSpec; + $this->singularGetTestGenerator = new SingularGetRequestTestGenerator; + $this->deleteTestGenerator = new DeleteRequestTestGenerator; } /** @@ -71,7 +67,7 @@ protected function shouldIncludeEndpoint(Endpoint $endpoint): bool */ protected function getTestStubPath(): string { - return __DIR__.'/stubs/pest-resource-test.stub'; + return __DIR__.'/TestGenerators/stubs/pest-resource-test.stub'; } /** @@ -89,7 +85,17 @@ protected function getTestFunctionStubPath(Endpoint $endpoint): string return $this->mutationTestGenerator->getStubPath(); } - return __DIR__.'/stubs/pest-resource-test-func.stub'; + // Delegate to SingularGetRequestTestGenerator for singular GET requests + if ($this->singularGetTestGenerator->isSingularGetRequest($endpoint)) { + return $this->singularGetTestGenerator->getStubPath(); + } + + // Delegate to DeleteRequestTestGenerator for DELETE requests + if ($this->deleteTestGenerator->isDeleteRequest($endpoint)) { + return $this->deleteTestGenerator->getStubPath(); + } + + throw \Exception('Unmatched request type'); } /** @@ -150,129 +156,25 @@ protected function replaceAdditionalStubVariables( ): string { // Delegate to CollectionRequestTestGenerator for collection requests if ($this->collectionTestGenerator->isCollectionRequest($endpoint)) { - $functionStub = $this->collectionTestGenerator->replaceStubVariables($functionStub, $endpoint); + return $this->collectionTestGenerator->replaceStubVariables($functionStub, $endpoint); } // Delegate to MutationRequestTestGenerator for mutation requests if ($this->mutationTestGenerator->isMutationRequest($endpoint)) { - $functionStub = $this->mutationTestGenerator->replaceStubVariables($functionStub, $endpoint); - - // Return early - mutation generator handles everything - return $functionStub; + return $this->mutationTestGenerator->replaceStubVariables($functionStub, $endpoint); } - // Generate mock data for GET requests (without validation) - if ($endpoint->method->isGet()) { - // Replace fixture with inline mock data - // Match both the template variable and actual fixture names - $mockResponseBody = $this->generateMockResponseBody($endpoint); - $functionStub = preg_replace( - "/MockResponse::fixture\('[^']+'\)/", - "MockResponse::make($mockResponseBody, 200)", - $functionStub - ); - } else { - // For non-GET requests (POST, PATCH, DELETE), use simple 200 response - $functionStub = preg_replace( - "/MockResponse::fixture\('[^']+'\)/", - 'MockResponse::make([], 200)', - $functionStub - ); + // Delegate to SingularGetRequestTestGenerator for singular GET requests + if ($this->singularGetTestGenerator->isSingularGetRequest($endpoint)) { + return $this->singularGetTestGenerator->replaceStubVariables($functionStub, $endpoint); } - return $functionStub; - } - - /** - * Find an endpoint specification by operation ID - */ - protected function findEndpointSpecByOperationId(string $operationId): ?array - { - $spec = $this->getOpenApiSpec(); - if (empty($spec) || ! isset($spec['paths'])) { - return null; - } - - // Search through all paths and methods to find the matching operationId - foreach ($spec['paths'] as $path => $pathItem) { - foreach ($pathItem as $method => $operation) { - // Skip non-operation keys (like parameters, summary, etc.) - if (! is_array($operation) || ! isset($operation['operationId'])) { - continue; - } - - if ($operation['operationId'] === $operationId) { - return $operation; - } - } + // Delegate to DeleteRequestTestGenerator for DELETE requests + if ($this->deleteTestGenerator->isDeleteRequest($endpoint)) { + return $this->deleteTestGenerator->replaceStubVariables($functionStub, $endpoint); } - return null; - } - - /** - * Extract example data from OpenAPI spec for an endpoint - */ - protected function extractExampleFromSpec(Endpoint $endpoint): ?array - { - $spec = $this->getOpenApiSpec(); - if (empty($spec)) { - return null; - } - - // Find the endpoint spec by operationId - $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); - if (! $endpointSpec) { - return null; - } - - // Try to find examples in this order: - // 1. Response-level example: responses.200.content.application/json.example - if (isset($endpointSpec['responses']['200']['content']['application/json']['example'])) { - return $endpointSpec['responses']['200']['content']['application/json']['example']; - } - - // 2. Response-level examples array (use first one) - if (isset($endpointSpec['responses']['200']['content']['application/json']['examples'])) { - $examples = $endpointSpec['responses']['200']['content']['application/json']['examples']; - $firstExample = reset($examples); - if (isset($firstExample['value'])) { - return $firstExample['value']; - } - } - - // 3. Schema-level example - if (isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { - $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; - - // Check for direct example - if (isset($schema['example'])) { - return $schema['example']; - } - - // Check if schema references a component - if (isset($schema['$ref'])) { - $ref = $schema['$ref']; - $refPath = str_replace('#/', '', $ref); - $refParts = explode('/', $refPath); - - // Navigate to the referenced schema - $referencedSchema = $spec; - foreach ($refParts as $part) { - if (! isset($referencedSchema[$part])) { - return null; - } - $referencedSchema = $referencedSchema[$part]; - } - - // Check for example in referenced schema - if (isset($referencedSchema['example'])) { - return $referencedSchema['example']; - } - } - } - - return null; + throw new \Exception('Unmatched request type'); } /** @@ -339,185 +241,4 @@ protected function generateMockData(Endpoint $endpoint): array ], ]; } - - /** - * Get the response schema for an endpoint from the OpenAPI spec - */ - protected function getResponseSchemaForEndpoint(Endpoint $endpoint): ?array - { - $spec = $this->getOpenApiSpec(); - if (empty($spec)) { - return null; - } - - // Find the endpoint spec by operationId - $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); - if (! $endpointSpec || ! isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { - return null; - } - - $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; - - // Handle array responses (collections) - if (isset($schema['type']) && $schema['type'] === 'array' && isset($schema['items'])) { - $schema = $schema['items']; - } - - // Resolve $ref if present - if (isset($schema['$ref'])) { - $ref = $schema['$ref']; - $refPath = str_replace('#/', '', $ref); - $refParts = explode('/', $refPath); - - $referencedSchema = $spec; - foreach ($refParts as $part) { - if (! isset($referencedSchema[$part])) { - return null; - } - $referencedSchema = $referencedSchema[$part]; - } - - return $referencedSchema; - } - - return $schema; - } - - /** - * Generate mock attributes based on schema properties - */ - protected function generateMockAttributes(array $schema): array - { - $attributes = []; - - // Check if this is a JSON:API schema with attributes object - if (isset($schema['properties']['attributes']['properties'])) { - $properties = $schema['properties']['attributes']['properties']; - } elseif (isset($schema['properties'])) { - $properties = $schema['properties']; - } else { - return ['name' => 'Mock value']; - } - - foreach ($properties as $propName => $propSpec) { - // Skip non-attribute fields - if (in_array($propName, ['id', 'type', 'attributes', 'relationships'])) { - continue; - } - - $attributes[$propName] = $this->getMockValueForProperty($propName, $propSpec); - } - - return $attributes; - } - - /** - * Generate a mock value based on property name and type - */ - protected function getMockValueForProperty(string $propertyName, array $propertySpec): mixed - { - // Check for example in property spec - if (isset($propertySpec['example'])) { - return $propertySpec['example']; - } - - $type = $propertySpec['type'] ?? 'string'; - $format = $propertySpec['format'] ?? null; - - // DateTime fields - if ($format === 'date-time' || str_contains($propertyName, 'At') || str_contains($propertyName, 'Date')) { - return '2025-01-15T10:30:00Z'; - } - - // ID fields - if (str_ends_with($propertyName, 'Id')) { - return 'mock-id-123'; - } - - // Email fields - if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { - return 'test@example.com'; - } - - // Type-based generation - return match ($type) { - 'boolean' => true, - 'integer', 'number' => 42, - 'string' => 'Mock value', - 'array' => [], - 'object' => [], - default => 'Mock value', - }; - } - - /** - * Get the resource type from a schema (e.g., "users", "entries") - */ - protected function getResourceTypeFromSchema(array $schema): string - { - // Try to extract from schema title or description - if (isset($schema['title'])) { - return NameHelper::safeVariableName($schema['title']); - } - - // Try to get from properties.type.example - if (isset($schema['properties']['type']['example'])) { - return $schema['properties']['type']['example']; - } - - // Fallback to generic name - return 'resources'; - } - - /** - * Generate the complete mock response body for an endpoint - */ - protected function generateMockResponseBody(Endpoint $endpoint): string - { - // Try to extract example from OpenAPI spec first - $example = $this->extractExampleFromSpec($endpoint); - - if ($example !== null) { - // Use example from spec - $mockData = $example; - } else { - // Generate fallback mock data - $mockData = $this->generateMockData($endpoint); - } - - // Format as PHP array syntax for the stub - return $this->formatArrayAsPhp($mockData); - } - - /** - * Format an array as PHP code string - */ - protected function formatArrayAsPhp(array $data, int $indent = 0): string - { - $indentStr = str_repeat(' ', $indent); - $lines = []; - - foreach ($data as $key => $value) { - $keyStr = is_string($key) ? "'$key'" : $key; - - if (is_array($value)) { - $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; - } elseif (is_string($value)) { - $escapedValue = addslashes($value); - $lines[] = $indentStr." $keyStr => '$escapedValue',"; - } elseif (is_bool($value)) { - $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; - } elseif (is_null($value)) { - $lines[] = $indentStr." $keyStr => null,"; - } else { - $lines[] = $indentStr." $keyStr => $value,"; - } - } - - if (empty($lines)) { - return '[]'; - } - - return "[\n".implode("\n", $lines)."\n$indentStr]"; - } } diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index d98cb7d..af0ffc7 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -6,9 +6,18 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; class CollectionRequestTestGenerator { + use MockDataGeneratorTrait; + use OpenApiSpecLoaderTrait; + use SchemaExtractorTrait; + use TestValueGeneratorTrait; + /** * Check if endpoint is a GET collection request (implements Paginatable) */ @@ -46,9 +55,59 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): $nonFilterParams = $this->getNonFilterQueryParameters($endpoint); $functionStub = str_replace('{{ nonFilterParams }}', $nonFilterParams, $functionStub); + // Replace fixture with inline mock data + $mockResponseBody = $this->generateMockResponseBody($endpoint); + $functionStub = preg_replace( + "/MockResponse::fixture\('[^']+'\)/", + "MockResponse::make($mockResponseBody, 200)", + $functionStub + ); + return $functionStub; } + /** + * Generate mock data for collection response + */ + protected function generateMockData(Endpoint $endpoint): array + { + // Try to determine the schema for this endpoint + $schema = $this->getResponseSchemaForEndpoint($endpoint); + + if ($schema) { + // Generate mock data based on schema + $attributes = $this->generateMockAttributes($schema); + $resourceType = $this->getResourceTypeFromSchema($schema); + + // Generate 2-3 items for collections + return [ + 'data' => [ + [ + 'type' => $resourceType, + 'id' => 'mock-id-1', + 'attributes' => $attributes, + ], + [ + 'type' => $resourceType, + 'id' => 'mock-id-2', + 'attributes' => $this->generateMockAttributes($schema), + ], + ], + ]; + } + + // Fallback: generic mock data + $resourceName = NameHelper::resourceClassName($endpoint->collection); + $resourceType = NameHelper::safeVariableName($resourceName); + + return [ + 'data' => [ + ['type' => $resourceType, 'id' => 'mock-id-1', 'attributes' => ['name' => 'Mock item 1']], + ['type' => $resourceType, 'id' => 'mock-id-2', 'attributes' => ['name' => 'Mock item 2']], + ], + ]; + } + /** * Generate the complete filter assertion block */ @@ -152,28 +211,4 @@ protected function getNonFilterQueryParameters(Endpoint $endpoint): string return implode(', ', $params); } - - /** - * Generate appropriate test value based on property name - */ - protected function generateTestValueForProperty(string $property): string - { - // Date/time properties - if (str_contains($property, 'At') || str_contains($property, 'Date')) { - return "'2025-01-01'"; - } - - // ID properties - if (str_ends_with($property, 'Id')) { - return "'test-id-123'"; - } - - // Boolean properties - if (str_starts_with($property, 'is') || str_starts_with($property, 'has')) { - return 'true'; - } - - // Default to string - return "'test-value'"; - } } diff --git a/generator/TestGenerators/DeleteRequestTestGenerator.php b/generator/TestGenerators/DeleteRequestTestGenerator.php new file mode 100644 index 0000000..7c81430 --- /dev/null +++ b/generator/TestGenerators/DeleteRequestTestGenerator.php @@ -0,0 +1,35 @@ +method->isDelete(); + } + + /** + * Get the stub path for DELETE request tests + */ + public function getStubPath(): string + { + return __DIR__.'/stubs/pest-delete-request-test-func.stub'; + } + + /** + * Replace stub variables (DELETE requests don't need custom replacements) + */ + public function replaceStubVariables(string $functionStub, Endpoint $endpoint): string + { + // DELETE requests use the standard stub without custom replacements + return $functionStub; + } +} diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 4d88b06..787571f 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -6,10 +6,15 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; class MutationRequestTestGenerator { - protected ?array $openApiSpec = null; + use OpenApiSpecLoaderTrait; + use SchemaExtractorTrait; + use TestValueGeneratorTrait; /** * Check if endpoint is a mutation request (POST or PATCH) @@ -193,221 +198,6 @@ protected function getDtoClassName(Endpoint $endpoint): string return NameHelper::resourceClassName($name); } - /** - * Get the resource type for JSON:API (plural, lowercase) - */ - protected function getResourceTypeFromEndpoint(Endpoint $endpoint): string - { - if ($endpoint->collection) { - return NameHelper::safeVariableName($endpoint->collection); - } - - // Fallback: parse from endpoint path - $path = $endpoint->path; - // Extract first path segment (e.g., /budgets -> budgets) - preg_match('#^/([^/]+)#', $path, $matches); - - return $matches[1] ?? 'resources'; - } - - /** - * Load and cache the OpenAPI specification - */ - protected function getOpenApiSpec(): array - { - if ($this->openApiSpec === null) { - $specPath = __DIR__.'/../openapi.json'; - if (file_exists($specPath)) { - $this->openApiSpec = json_decode(file_get_contents($specPath), true); - } else { - $this->openApiSpec = []; - } - } - - return $this->openApiSpec; - } - - /** - * Get the request body schema for an endpoint from the OpenAPI spec - */ - protected function getRequestSchemaForEndpoint(Endpoint $endpoint): ?array - { - $spec = $this->getOpenApiSpec(); - if (empty($spec)) { - return null; - } - - // Find the endpoint spec by operationId - $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); - if (! $endpointSpec || ! isset($endpointSpec['requestBody']['content']['application/json']['schema'])) { - return null; - } - - $schema = $endpointSpec['requestBody']['content']['application/json']['schema']; - - // Resolve $ref if present - if (isset($schema['$ref'])) { - $schema = $this->resolveSchemaReference($schema['$ref']); - } - - return $schema; - } - - /** - * Find an endpoint specification by operation ID - */ - protected function findEndpointSpecByOperationId(string $operationId): ?array - { - $spec = $this->getOpenApiSpec(); - if (empty($spec) || ! isset($spec['paths'])) { - return null; - } - - // Search through all paths and methods to find the matching operationId - foreach ($spec['paths'] as $path => $pathItem) { - foreach ($pathItem as $method => $operation) { - // Skip non-operation keys (like parameters, summary, etc.) - if (! is_array($operation) || ! isset($operation['operationId'])) { - continue; - } - - if ($operation['operationId'] === $operationId) { - return $operation; - } - } - } - - return null; - } - - /** - * Resolve a schema reference ($ref) - */ - protected function resolveSchemaReference(string $ref): ?array - { - $spec = $this->getOpenApiSpec(); - $refPath = str_replace('#/', '', $ref); - $refParts = explode('/', $refPath); - - $referencedSchema = $spec; - foreach ($refParts as $part) { - if (! isset($referencedSchema[$part])) { - return null; - } - $referencedSchema = $referencedSchema[$part]; - } - - return $referencedSchema; - } - - /** - * Extract properties from a schema (handling JSON:API structure) - */ - protected function extractPropertiesFromSchema(array $schema): array - { - // For JSON:API, properties are nested in data.attributes - if (isset($schema['properties']['data']['properties']['attributes']['properties'])) { - return $schema['properties']['data']['properties']['attributes']['properties']; - } - - // Fallback: check if properties has attributes - if (isset($schema['properties']['attributes']['properties'])) { - return $schema['properties']['attributes']['properties']; - } - - // Direct properties - if (isset($schema['properties'])) { - $properties = $schema['properties']; - // Remove non-attribute fields - unset($properties['id'], $properties['type'], $properties['attributes'], $properties['relationships']); - - return $properties; - } - - return []; - } - - /** - * Generate appropriate test value based on property name and spec - */ - protected function generateTestValueForProperty(string $property, array $spec = []): string - { - // Check for example in spec - if (isset($spec['example'])) { - $value = $spec['example']; - if (is_string($value)) { - return "'{$value}'"; - } - if (is_bool($value)) { - return $value ? 'true' : 'false'; - } - if (is_numeric($value)) { - return (string) $value; - } - } - - $type = $spec['type'] ?? 'string'; - $format = $spec['format'] ?? null; - - // DateTime fields - if ($format === 'date-time' || str_contains($property, 'At') || str_contains($property, 'Date')) { - return "'2025-01-01T10:00:00Z'"; - } - - // ID properties - if (str_ends_with($property, 'Id')) { - return "'test-id-123'"; - } - - // Boolean properties - if ($type === 'boolean' || str_starts_with($property, 'is') || str_starts_with($property, 'has')) { - return 'false'; - } - - // Numeric properties - if ($type === 'integer' || $type === 'number') { - return '123'; - } - - // String properties with common names - if ($property === 'title') { - return "'test title'"; - } - if ($property === 'description') { - return "'test description'"; - } - if ($property === 'name') { - return "'test name'"; - } - - // Default to string - return "'test value'"; - } - - /** - * Format a value for use in assertions - */ - protected function formatValueForAssertion(string $value): string - { - // If value is already quoted, return as-is - if (str_starts_with($value, "'") && str_ends_with($value, "'")) { - return $value; - } - - // Boolean values - if ($value === 'true' || $value === 'false') { - return $value; - } - - // Numeric values - if (is_numeric($value)) { - return $value; - } - - // Wrap in quotes - return "'{$value}'"; - } - /** * Generate fallback body validation when schema is not available */ diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php new file mode 100644 index 0000000..0c0f959 --- /dev/null +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -0,0 +1,86 @@ +method->isGet() && ! empty($endpoint->pathParameters); + } + + /** + * Get the stub path for singular GET request tests + */ + public function getStubPath(): string + { + return __DIR__.'/stubs/pest-singular-get-request-test-func.stub'; + } + + /** + * Replace stub variables with singular GET-specific content + */ + public function replaceStubVariables(string $functionStub, Endpoint $endpoint): string + { + // Generate mock response body + $mockResponseBody = $this->generateMockResponseBody($endpoint); + $functionStub = str_replace( + '{{ mockResponse }}', + "MockResponse::make($mockResponseBody, 200)", + $functionStub + ); + + return $functionStub; + } + + /** + * Generate mock data for singular GET response + */ + protected function generateMockData(Endpoint $endpoint): array + { + // Try to determine the schema for this endpoint + $schema = $this->getResponseSchemaForEndpoint($endpoint); + + if ($schema) { + // Generate mock data based on schema + $attributes = $this->generateMockAttributes($schema); + $resourceType = $this->getResourceTypeFromSchema($schema); + + return [ + 'data' => [ + 'type' => $resourceType, + 'id' => 'mock-id-123', + 'attributes' => $attributes, + ], + ]; + } + + // Fallback: generic mock data + $resourceName = NameHelper::resourceClassName($endpoint->collection); + $resourceType = NameHelper::safeVariableName($resourceName); + + return [ + 'data' => [ + 'type' => $resourceType, + 'id' => 'mock-id-123', + 'attributes' => ['name' => 'Mock item'], + ], + ]; + } +} diff --git a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php new file mode 100644 index 0000000..34098aa --- /dev/null +++ b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php @@ -0,0 +1,183 @@ +getOpenApiSpec(); + if (empty($spec)) { + return null; + } + + // Find the endpoint spec by operationId + $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); + if (! $endpointSpec) { + return null; + } + + // Try to find examples in this order: + // 1. Response-level example: responses.200.content.application/json.example + if (isset($endpointSpec['responses']['200']['content']['application/json']['example'])) { + return $endpointSpec['responses']['200']['content']['application/json']['example']; + } + + // 2. Response-level examples array (use first one) + if (isset($endpointSpec['responses']['200']['content']['application/json']['examples'])) { + $examples = $endpointSpec['responses']['200']['content']['application/json']['examples']; + $firstExample = reset($examples); + if (isset($firstExample['value'])) { + return $firstExample['value']; + } + } + + // 3. Schema-level example + if (isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { + $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; + + // Check for direct example + if (isset($schema['example'])) { + return $schema['example']; + } + + // Check if schema references a component + if (isset($schema['$ref'])) { + $schema = $this->resolveSchemaReference($schema['$ref']); + + // Check for example in referenced schema + if (isset($schema['example'])) { + return $schema['example']; + } + } + } + + return null; + } + + /** + * Generate mock attributes based on schema properties + */ + protected function generateMockAttributes(array $schema): array + { + $attributes = []; + + // Check if this is a JSON:API schema with attributes object + if (isset($schema['properties']['attributes']['properties'])) { + $properties = $schema['properties']['attributes']['properties']; + } elseif (isset($schema['properties'])) { + $properties = $schema['properties']; + } else { + return ['name' => 'Mock value']; + } + + foreach ($properties as $propName => $propSpec) { + // Skip non-attribute fields + if (in_array($propName, ['id', 'type', 'attributes', 'relationships'])) { + continue; + } + + $attributes[$propName] = $this->getMockValueForProperty($propName, $propSpec); + } + + return $attributes; + } + + /** + * Generate a mock value based on property name and type + */ + protected function getMockValueForProperty(string $propertyName, array $propertySpec): mixed + { + // Check for example in property spec + if (isset($propertySpec['example'])) { + return $propertySpec['example']; + } + + $type = $propertySpec['type'] ?? 'string'; + $format = $propertySpec['format'] ?? null; + + // DateTime fields + if ($format === 'date-time' || str_contains($propertyName, 'At') || str_contains($propertyName, 'Date')) { + return '2025-01-15T10:30:00Z'; + } + + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return 'mock-id-123'; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return 'test@example.com'; + } + + // Type-based generation + return match ($type) { + 'boolean' => true, + 'integer', 'number' => 42, + 'string' => 'Mock value', + 'array' => [], + 'object' => [], + default => 'Mock value', + }; + } + + /** + * Generate the complete mock response body for an endpoint + */ + protected function generateMockResponseBody(Endpoint $endpoint): string + { + // Try to extract example from OpenAPI spec first + $example = $this->extractExampleFromSpec($endpoint); + + if ($example !== null) { + // Use example from spec + $mockData = $example; + } else { + // Generate fallback mock data + $mockData = $this->generateMockData($endpoint); + } + + // Format as PHP array syntax for the stub + return $this->formatArrayAsPhp($mockData); + } + + /** + * Format an array as PHP code string + */ + protected function formatArrayAsPhp(array $data, int $indent = 0): string + { + $indentStr = str_repeat(' ', $indent); + $lines = []; + + foreach ($data as $key => $value) { + $keyStr = is_string($key) ? "'$key'" : $key; + + if (is_array($value)) { + $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; + } elseif (is_string($value)) { + $escapedValue = addslashes($value); + $lines[] = $indentStr." $keyStr => '$escapedValue',"; + } elseif (is_bool($value)) { + $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; + } elseif (is_null($value)) { + $lines[] = $indentStr." $keyStr => null,"; + } else { + $lines[] = $indentStr." $keyStr => $value,"; + } + } + + if (empty($lines)) { + return '[]'; + } + + return "[\n".implode("\n", $lines)."\n$indentStr]"; + } +} diff --git a/generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php b/generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php new file mode 100644 index 0000000..5adf296 --- /dev/null +++ b/generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php @@ -0,0 +1,74 @@ +openApiSpec === null) { + $specPath = __DIR__.'/../../../openapi.json'; + if (file_exists($specPath)) { + $this->openApiSpec = json_decode(file_get_contents($specPath), true); + } else { + $this->openApiSpec = []; + } + } + + return $this->openApiSpec; + } + + /** + * Find an endpoint specification by operation ID + */ + protected function findEndpointSpecByOperationId(string $operationId): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec) || ! isset($spec['paths'])) { + return null; + } + + // Search through all paths and methods to find the matching operationId + foreach ($spec['paths'] as $path => $pathItem) { + foreach ($pathItem as $method => $operation) { + // Skip non-operation keys (like parameters, summary, etc.) + if (! is_array($operation) || ! isset($operation['operationId'])) { + continue; + } + + if ($operation['operationId'] === $operationId) { + return $operation; + } + } + } + + return null; + } + + /** + * Resolve a schema reference ($ref) + */ + protected function resolveSchemaReference(string $ref): ?array + { + $spec = $this->getOpenApiSpec(); + $refPath = str_replace('#/', '', $ref); + $refParts = explode('/', $refPath); + + $referencedSchema = $spec; + foreach ($refParts as $part) { + if (! isset($referencedSchema[$part])) { + return null; + } + $referencedSchema = $referencedSchema[$part]; + } + + return $referencedSchema; + } +} diff --git a/generator/TestGenerators/Traits/SchemaExtractorTrait.php b/generator/TestGenerators/Traits/SchemaExtractorTrait.php new file mode 100644 index 0000000..ac9acdd --- /dev/null +++ b/generator/TestGenerators/Traits/SchemaExtractorTrait.php @@ -0,0 +1,131 @@ +getOpenApiSpec(); + if (empty($spec)) { + return null; + } + + // Find the endpoint spec by operationId + $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); + if (! $endpointSpec || ! isset($endpointSpec['requestBody']['content']['application/json']['schema'])) { + return null; + } + + $schema = $endpointSpec['requestBody']['content']['application/json']['schema']; + + // Resolve $ref if present + if (isset($schema['$ref'])) { + $schema = $this->resolveSchemaReference($schema['$ref']); + } + + return $schema; + } + + /** + * Get the response schema for an endpoint from the OpenAPI spec + */ + protected function getResponseSchemaForEndpoint(Endpoint $endpoint): ?array + { + $spec = $this->getOpenApiSpec(); + if (empty($spec)) { + return null; + } + + // Find the endpoint spec by operationId + $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); + if (! $endpointSpec || ! isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { + return null; + } + + $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; + + // Handle array responses (collections) + if (isset($schema['type']) && $schema['type'] === 'array' && isset($schema['items'])) { + $schema = $schema['items']; + } + + // Resolve $ref if present + if (isset($schema['$ref'])) { + $schema = $this->resolveSchemaReference($schema['$ref']); + } + + return $schema; + } + + /** + * Extract properties from a schema (handling JSON:API structure) + */ + protected function extractPropertiesFromSchema(array $schema): array + { + // For JSON:API, properties are nested in data.attributes + if (isset($schema['properties']['data']['properties']['attributes']['properties'])) { + return $schema['properties']['data']['properties']['attributes']['properties']; + } + + // Fallback: check if properties has attributes + if (isset($schema['properties']['attributes']['properties'])) { + return $schema['properties']['attributes']['properties']; + } + + // Direct properties + if (isset($schema['properties'])) { + $properties = $schema['properties']; + // Remove non-attribute fields + unset($properties['id'], $properties['type'], $properties['attributes'], $properties['relationships']); + + return $properties; + } + + return []; + } + + /** + * Get the resource type from a schema (e.g., "users", "entries") + */ + protected function getResourceTypeFromSchema(array $schema): string + { + // Try to extract from schema title or description + if (isset($schema['title'])) { + return NameHelper::safeVariableName($schema['title']); + } + + // Try to get from properties.type.example + if (isset($schema['properties']['type']['example'])) { + return $schema['properties']['type']['example']; + } + + // Fallback to generic name + return 'resources'; + } + + /** + * Get the resource type for JSON:API (plural, lowercase) + */ + protected function getResourceTypeFromEndpoint(Endpoint $endpoint): string + { + if ($endpoint->collection) { + return NameHelper::safeVariableName($endpoint->collection); + } + + // Fallback: parse from endpoint path + $path = $endpoint->path; + // Extract first path segment (e.g., /budgets -> budgets) + preg_match('#^/([^/]+)#', $path, $matches); + + return $matches[1] ?? 'resources'; + } +} diff --git a/generator/TestGenerators/Traits/TestValueGeneratorTrait.php b/generator/TestGenerators/Traits/TestValueGeneratorTrait.php new file mode 100644 index 0000000..81c2133 --- /dev/null +++ b/generator/TestGenerators/Traits/TestValueGeneratorTrait.php @@ -0,0 +1,89 @@ + MockResponse::fixture('{{ fixtureName }}'), + {{ requestClass }}::class => MockResponse::make([], 200), ]); $response = $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ methodArguments }}); diff --git a/generator/stubs/pest-filter-assertion-block.stub b/generator/TestGenerators/stubs/pest-filter-assertion-block.stub similarity index 100% rename from generator/stubs/pest-filter-assertion-block.stub rename to generator/TestGenerators/stubs/pest-filter-assertion-block.stub diff --git a/generator/stubs/pest-fluent-filter-test-func.stub b/generator/TestGenerators/stubs/pest-fluent-filter-test-func.stub similarity index 100% rename from generator/stubs/pest-fluent-filter-test-func.stub rename to generator/TestGenerators/stubs/pest-fluent-filter-test-func.stub diff --git a/generator/stubs/pest-jsonapi-test-func.stub b/generator/TestGenerators/stubs/pest-jsonapi-test-func.stub similarity index 100% rename from generator/stubs/pest-jsonapi-test-func.stub rename to generator/TestGenerators/stubs/pest-jsonapi-test-func.stub diff --git a/generator/stubs/pest-mutation-test-func.stub b/generator/TestGenerators/stubs/pest-mutation-test-func.stub similarity index 100% rename from generator/stubs/pest-mutation-test-func.stub rename to generator/TestGenerators/stubs/pest-mutation-test-func.stub diff --git a/generator/stubs/pest-resource-test.stub b/generator/TestGenerators/stubs/pest-resource-test.stub similarity index 100% rename from generator/stubs/pest-resource-test.stub rename to generator/TestGenerators/stubs/pest-resource-test.stub diff --git a/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub b/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub new file mode 100644 index 0000000..cd5f232 --- /dev/null +++ b/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub @@ -0,0 +1,11 @@ +it('{{ testDescription }}', function () { + Saloon::fake([ + {{ requestClass }}::class => {{ mockResponse }}, + ]); + + $response = $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ methodArguments }}); + + Saloon::assertSent({{ requestClass }}::class); + + expect($response->status())->toBe(200); +}); diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..0502b72 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,14 @@ + + + + + ./tests + + + + + + src + + + diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 20fa2ef..f586503 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -38,7 +38,7 @@ $request = (new GetBudgetsRequest(include: 'test string')) ->filter('customerId', 'test-id-123') ->filter('budgetTypeId', 'test-id-123') - ->filter('isArchived', true); + ->filter('isArchived', false); $response = $this->timaticConnector->send($request); @@ -50,7 +50,7 @@ expect($query)->toHaveKey('filter[customerId]', 'test-id-123'); expect($query)->toHaveKey('filter[budgetTypeId]', 'test-id-123'); - expect($query)->toHaveKey('filter[isArchived]', true); + expect($query)->toHaveKey('filter[isArchived]', false); return true; }); diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index f6d9b71..6fe3537 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -34,7 +34,7 @@ ]); $request = (new GetEntrySuggestionsRequest) - ->filter('date', 'test-value'); + ->filter('date', 'test value'); $response = $this->timaticConnector->send($request); @@ -44,7 +44,7 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[date]', 'test-value'); + expect($query)->toHaveKey('filter[date]', 'test value'); return true; }); diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 77fed9b..28e33b9 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -38,7 +38,7 @@ $request = (new GetEntriesRequest(include: 'test string')) ->filter('userId', 'test-id-123') ->filter('budgetId', 'test-id-123') - ->filter('startedAt', '2025-01-01'); + ->filter('startedAt', '2025-01-01T10:00:00Z'); $response = $this->timaticConnector->send($request); @@ -50,7 +50,7 @@ expect($query)->toHaveKey('filter[userId]', 'test-id-123'); expect($query)->toHaveKey('filter[budgetId]', 'test-id-123'); - expect($query)->toHaveKey('filter[startedAt]', '2025-01-01'); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-01T10:00:00Z'); return true; }); diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index 6b670de..35d3b9d 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -32,9 +32,9 @@ ]); $request = (new GetOvertimesRequest) - ->filter('startedAt', '2025-01-01') - ->filter('endedAt', '2025-01-01') - ->filter('isApproved', true); + ->filter('startedAt', '2025-01-01T10:00:00Z') + ->filter('endedAt', '2025-01-01T10:00:00Z') + ->filter('isApproved', false); $response = $this->timaticConnector->send($request); @@ -44,9 +44,9 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[startedAt]', '2025-01-01'); - expect($query)->toHaveKey('filter[endedAt]', '2025-01-01'); - expect($query)->toHaveKey('filter[isApproved]', true); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-01T10:00:00Z'); + expect($query)->toHaveKey('filter[endedAt]', '2025-01-01T10:00:00Z'); + expect($query)->toHaveKey('filter[isApproved]', false); return true; }); diff --git a/tests/Requests/UserCustomerHoursAggregateTest.php b/tests/Requests/UserCustomerHoursAggregateTest.php index ec6eafb..e46b050 100644 --- a/tests/Requests/UserCustomerHoursAggregateTest.php +++ b/tests/Requests/UserCustomerHoursAggregateTest.php @@ -32,8 +32,8 @@ ]); $request = (new GetUserCustomerHoursAggregatesRequest) - ->filter('startedAt', '2025-01-01') - ->filter('endedAt', '2025-01-01') + ->filter('startedAt', '2025-01-01T10:00:00Z') + ->filter('endedAt', '2025-01-01T10:00:00Z') ->filter('teamId', 'test-id-123'); $response = $this->timaticConnector->send($request); @@ -44,8 +44,8 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[startedAt]', '2025-01-01'); - expect($query)->toHaveKey('filter[endedAt]', '2025-01-01'); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-01T10:00:00Z'); + expect($query)->toHaveKey('filter[endedAt]', '2025-01-01T10:00:00Z'); expect($query)->toHaveKey('filter[teamId]', 'test-id-123'); return true; From 149dacc279f6f147fad8475d8a25856077da89bb Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 01:32:54 +0100 Subject: [PATCH 31/62] consistent stub naming --- .../CollectionRequestTestGenerator.php | 2 +- ...=> pest-collection-request-test-func.stub} | 0 .../stubs/pest-jsonapi-test-func.stub | 36 ------------------- 3 files changed, 1 insertion(+), 37 deletions(-) rename generator/TestGenerators/stubs/{pest-fluent-filter-test-func.stub => pest-collection-request-test-func.stub} (100%) delete mode 100644 generator/TestGenerators/stubs/pest-jsonapi-test-func.stub diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index af0ffc7..19a5d9e 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -32,7 +32,7 @@ public function isCollectionRequest(Endpoint $endpoint): bool */ public function getStubPath(): string { - return __DIR__.'/stubs/pest-fluent-filter-test-func.stub'; + return __DIR__.'/stubs/pest-collection-request-test-func.stub'; } /** diff --git a/generator/TestGenerators/stubs/pest-fluent-filter-test-func.stub b/generator/TestGenerators/stubs/pest-collection-request-test-func.stub similarity index 100% rename from generator/TestGenerators/stubs/pest-fluent-filter-test-func.stub rename to generator/TestGenerators/stubs/pest-collection-request-test-func.stub diff --git a/generator/TestGenerators/stubs/pest-jsonapi-test-func.stub b/generator/TestGenerators/stubs/pest-jsonapi-test-func.stub deleted file mode 100644 index d623413..0000000 --- a/generator/TestGenerators/stubs/pest-jsonapi-test-func.stub +++ /dev/null @@ -1,36 +0,0 @@ -it('{{ testDescription }}', function () { - $mockClient = new MockClient([ - {{ requestClass }}::class => MockResponse::make(status: {{ responseStatus }}), - ]); - -{{ modelSetup }} - $response = $this->{{ clientName }} - ->withMockClient($mockClient) - ->{{ resourceName }}() - ->{{ methodName }}({{ methodArguments }}); - - // Assert request was sent - $mockClient->assertSent({{ requestClass }}::class); - - // Validate JSON:API body structure - $mockClient->assertSent(function (Request $request) { - $body = $request->body()->all(); - - // Check JSON:API structure - if (!isset($body['data'])) { - return false; - } - - $data = $body['data']; - - // Validate required JSON:API fields - if (!isset($data['type']) || !isset($data['attributes'])) { - return false; - } - -{{ bodyAssertions }} - return true; - }); - - expect($response->status())->toBe({{ responseStatus }}); -}); From 23b27c379bc063c3f84d0ab7e8fbb8fe391acdd1 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 10:11:54 +0100 Subject: [PATCH 32/62] remove unused traits from base test generator --- generator/JsonApiPestTestGenerator.php | 72 -------------------------- 1 file changed, 72 deletions(-) diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index d65723c..8add364 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -12,16 +12,9 @@ use Timatic\SDK\Generator\TestGenerators\DeleteRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\MutationRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\SingularGetRequestTestGenerator; -use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; class JsonApiPestTestGenerator extends PestTestGenerator { - use MockDataGeneratorTrait; - use OpenApiSpecLoaderTrait; - use SchemaExtractorTrait; - protected CollectionRequestTestGenerator $collectionTestGenerator; protected MutationRequestTestGenerator $mutationTestGenerator; @@ -176,69 +169,4 @@ protected function replaceAdditionalStubVariables( throw new \Exception('Unmatched request type'); } - - /** - * Generate mock data based on property types (fallback when no examples exist) - */ - protected function generateMockData(Endpoint $endpoint): array - { - $spec = $this->getOpenApiSpec(); - $isCollection = $this->collectionTestGenerator->isCollectionRequest($endpoint); - - // Try to determine the schema for this endpoint - $schema = $this->getResponseSchemaForEndpoint($endpoint); - - if ($schema) { - // Generate mock data based on schema - $attributes = $this->generateMockAttributes($schema); - $resourceType = $this->getResourceTypeFromSchema($schema); - - if ($isCollection) { - // Generate 2-3 items for collections - return [ - 'data' => [ - [ - 'type' => $resourceType, - 'id' => 'mock-id-1', - 'attributes' => $attributes, - ], - [ - 'type' => $resourceType, - 'id' => 'mock-id-2', - 'attributes' => $this->generateMockAttributes($schema), - ], - ], - ]; - } - - return [ - 'data' => [ - 'type' => $resourceType, - 'id' => 'mock-id-123', - 'attributes' => $attributes, - ], - ]; - } - - // Fallback: generic mock data - $resourceName = NameHelper::resourceClassName($endpoint->collection); - $resourceType = NameHelper::safeVariableName($resourceName); - - if ($isCollection) { - return [ - 'data' => [ - ['type' => $resourceType, 'id' => 'mock-id-1', 'attributes' => ['name' => 'Mock item 1']], - ['type' => $resourceType, 'id' => 'mock-id-2', 'attributes' => ['name' => 'Mock item 2']], - ], - ]; - } - - return [ - 'data' => [ - 'type' => $resourceType, - 'id' => 'mock-id-123', - 'attributes' => ['name' => 'Mock item'], - ], - ]; - } } From ec94bbe800956b3ae3b818019532a271595a669d Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 10:45:00 +0100 Subject: [PATCH 33/62] clean up singular test generator --- .../SingularGetRequestTestGenerator.php | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index 0c0f959..8068a3a 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -5,7 +5,6 @@ namespace Timatic\SDK\Generator\TestGenerators; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; -use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; @@ -57,29 +56,19 @@ protected function generateMockData(Endpoint $endpoint): array // Try to determine the schema for this endpoint $schema = $this->getResponseSchemaForEndpoint($endpoint); - if ($schema) { - // Generate mock data based on schema - $attributes = $this->generateMockAttributes($schema); - $resourceType = $this->getResourceTypeFromSchema($schema); - - return [ - 'data' => [ - 'type' => $resourceType, - 'id' => 'mock-id-123', - 'attributes' => $attributes, - ], - ]; + if (! $schema) { + throw new \Exception('schema operation not found'); } - // Fallback: generic mock data - $resourceName = NameHelper::resourceClassName($endpoint->collection); - $resourceType = NameHelper::safeVariableName($resourceName); + // Generate mock data based on schema + $attributes = $this->generateMockAttributes($schema); + $resourceType = $this->getResourceTypeFromSchema($schema); return [ 'data' => [ 'type' => $resourceType, 'id' => 'mock-id-123', - 'attributes' => ['name' => 'Mock item'], + 'attributes' => $attributes, ], ]; } From 06ec4013e760258d8ef3d246e044b26724811445 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 11:20:02 +0100 Subject: [PATCH 34/62] use ApiSpecification and DTO's for tests --- claude-todo.md | 10 +- generator/JsonApiPestTestGenerator.php | 26 ++- .../CollectionRequestTestGenerator.php | 10 +- .../DeleteRequestTestGenerator.php | 8 + .../MutationRequestTestGenerator.php | 158 +++++++++++++-- .../SingularGetRequestTestGenerator.php | 10 +- .../Traits/MockDataGeneratorTrait.php | 88 ++++---- .../Traits/OpenApiSpecLoaderTrait.php | 74 ------- .../Traits/SchemaExtractorTrait.php | 191 +++++++++++++----- generator/generate.php | 28 ++- tests/Requests/ApproveTest.php | 10 +- tests/Requests/BudgetTest.php | 34 ++-- tests/Requests/CorrectionTest.php | 25 +-- tests/Requests/CustomerTest.php | 26 +-- tests/Requests/EntrySuggestionTest.php | 2 +- tests/Requests/EntryTest.php | 10 +- tests/Requests/EventTest.php | 16 +- tests/Requests/MarkAsExportedTest.php | 10 +- tests/Requests/MarkAsInvoicedTest.php | 10 +- tests/Requests/TeamTest.php | 26 +-- tests/Requests/UserTest.php | 16 +- 21 files changed, 482 insertions(+), 306 deletions(-) delete mode 100644 generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php diff --git a/claude-todo.md b/claude-todo.md index 51a4176..d36c496 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -1,10 +1,4 @@ -Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. - - -## Task 1 - Extract - -- Can we extract Singular GET Test logic from JsonAPIPestGenerator and Delete test logic into their own generator -classes? Perhaps all four classes can share some logic using traits +Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. ## Task 3: Validate DTO Hydration in GET Tests @@ -23,4 +17,6 @@ classes? Perhaps all four classes can share some logic using traits - Make a plan how we can add filters like $timaticConnector->budget->getBudgets(['key'=>'value']); - or come up with a better way to add filters to resources +## Gebruik raw JSON alleen om operationId → schema reference mapping te vinden (totdat we vendor package kunnen patchen) + **Note:** Each task is independent and can be completed, tested, and committed separately. \ No newline at end of file diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 8add364..2f93c1c 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -4,10 +4,14 @@ namespace Timatic\SDK\Generator; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; +use Crescat\SaloonSdkGenerator\Data\Generator\Config; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\PestTestGenerator; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Nette\PhpGenerator\PhpFile; use Timatic\SDK\Generator\TestGenerators\CollectionRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\DeleteRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\MutationRequestTestGenerator; @@ -23,12 +27,22 @@ class JsonApiPestTestGenerator extends PestTestGenerator protected DeleteRequestTestGenerator $deleteTestGenerator; - public function __construct() - { - $this->collectionTestGenerator = new CollectionRequestTestGenerator; - $this->mutationTestGenerator = new MutationRequestTestGenerator; - $this->singularGetTestGenerator = new SingularGetRequestTestGenerator; - $this->deleteTestGenerator = new DeleteRequestTestGenerator; + /** + * Override process() to instantiate test generators with ApiSpecification + */ + public function process( + Config $config, + ApiSpecification $specification, + GeneratedCode $generatedCode, + ): PhpFile|array|null { + // Instantiate test generators with the parsed ApiSpecification + $this->collectionTestGenerator = new CollectionRequestTestGenerator($specification); + $this->mutationTestGenerator = new MutationRequestTestGenerator($specification); + $this->singularGetTestGenerator = new SingularGetRequestTestGenerator($specification); + $this->deleteTestGenerator = new DeleteRequestTestGenerator($specification); + + // Call parent to continue normal processing + return parent::process($config, $specification, $generatedCode); } /** diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 19a5d9e..4c73414 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -4,20 +4,26 @@ namespace Timatic\SDK\Generator\TestGenerators; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; class CollectionRequestTestGenerator { use MockDataGeneratorTrait; - use OpenApiSpecLoaderTrait; use SchemaExtractorTrait; use TestValueGeneratorTrait; + protected ApiSpecification $specification; + + public function __construct(ApiSpecification $specification) + { + $this->specification = $specification; + } + /** * Check if endpoint is a GET collection request (implements Paginatable) */ diff --git a/generator/TestGenerators/DeleteRequestTestGenerator.php b/generator/TestGenerators/DeleteRequestTestGenerator.php index 7c81430..b8724ad 100644 --- a/generator/TestGenerators/DeleteRequestTestGenerator.php +++ b/generator/TestGenerators/DeleteRequestTestGenerator.php @@ -4,10 +4,18 @@ namespace Timatic\SDK\Generator\TestGenerators; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; class DeleteRequestTestGenerator { + protected ApiSpecification $specification; + + public function __construct(ApiSpecification $specification) + { + $this->specification = $specification; + } + /** * Check if endpoint is a DELETE request */ diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 787571f..006814f 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -4,18 +4,25 @@ namespace Timatic\SDK\Generator\TestGenerators; +use cebe\openapi\spec\Schema; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; class MutationRequestTestGenerator { - use OpenApiSpecLoaderTrait; use SchemaExtractorTrait; use TestValueGeneratorTrait; + protected ApiSpecification $specification; + + public function __construct(ApiSpecification $specification) + { + $this->specification = $specification; + } + /** * Check if endpoint is a mutation request (POST or PATCH) */ @@ -96,38 +103,138 @@ protected function generateDtoInstantiation(Endpoint $endpoint): string */ protected function generateDtoProperties(Endpoint $endpoint): string { - $schema = $this->getRequestSchemaForEndpoint($endpoint); - if (! $schema) { + $dtoClassName = $this->getDtoClassName($endpoint); + $properties = $this->getDtoPropertiesViaReflection($dtoClassName); + + if (empty($properties)) { return " \$dto->name = 'test value';"; } - $properties = $this->extractPropertiesFromSchema($schema); $lines = []; - // Limit to first 4 properties for the test - $propertiesToShow = array_slice($properties, 0, 4); + // Limit to first 4 properties for the test, skip timestamp fields + $count = 0; + foreach ($properties as $propInfo) { + if ($count >= 4) { + break; + } + + $propName = $propInfo['name']; + + // Skip read-only/auto-managed fields + if (in_array($propName, ['id', 'createdAt', 'updatedAt', 'deletedAt'])) { + continue; + } - foreach ($propertiesToShow as $propName => $propSpec) { - $value = $this->generateTestValueForProperty($propName, $propSpec); + $value = $this->generateTestValueForProperty($propName, $propInfo['type']); $lines[] = " \$dto->{$propName} = {$value};"; + $count++; + } + + // Fallback if no properties after filtering + if (empty($lines)) { + return " \$dto->name = 'test value';"; } return implode("\n", $lines); } + /** + * Get DTO properties via PHP reflection (DTO classes now exist on disk) + * + * @return array + */ + protected function getDtoPropertiesViaReflection(string $dtoClassName): array + { + $fullClassName = "\\Timatic\\SDK\\Dto\\{$dtoClassName}"; + + // Check if class exists (it should, because we dumped autoload) + if (! class_exists($fullClassName)) { + return []; + } + + $properties = []; + + try { + $reflection = new \ReflectionClass($fullClassName); + $reflectionProperties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC); + + foreach ($reflectionProperties as $property) { + // Skip static properties + if ($property->isStatic()) { + continue; + } + + $type = $property->getType(); + $typeName = null; + + if ($type instanceof \ReflectionNamedType) { + $typeName = ($type->allowsNull() ? '?' : '').$type->getName(); + } + + $properties[$property->getName()] = [ + 'name' => $property->getName(), + 'type' => $typeName, + ]; + } + } catch (\ReflectionException $e) { + return []; + } + + return $properties; + } + + /** + * Generate test value for a DTO property + */ + protected function generateTestValueForProperty(string $propertyName, ?string $typeName): string + { + if (! $typeName) { + return "'test value'"; + } + + // Normalize type name (remove nullable prefix) + $typeName = ltrim($typeName, '?'); + + // DateTime fields + if (str_contains($typeName, 'Carbon') || str_contains($typeName, 'DateTime')) { + return "'2025-01-15T10:30:00Z'"; + } + + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return "'mock-id-123'"; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return "'test@example.com'"; + } + + // Type-based generation + return match ($typeName) { + 'bool' => 'true', + 'int' => '42', + 'float' => '3.14', + 'string' => "'test value'", + 'array' => '[]', + default => "'test value'", + }; + } + /** * Generate body validation code */ protected function generateBodyValidation(Endpoint $endpoint): string { $resourceType = $this->getResourceTypeFromEndpoint($endpoint); - $schema = $this->getRequestSchemaForEndpoint($endpoint); + $dtoClassName = $this->getDtoClassName($endpoint); + $properties = $this->getDtoPropertiesViaReflection($dtoClassName); - if (! $schema) { + if (empty($properties)) { return $this->generateFallbackBodyValidation($resourceType, $endpoint); } - $properties = $this->extractPropertiesFromSchema($schema); $lines = []; $lines[] = ' $mockClient->assertSent(function (Request $request) {'; @@ -142,7 +249,7 @@ protected function generateBodyValidation(Endpoint $endpoint): string $lines[] = " ->data->type->toBe('{$resourceType}')"; // Generate attribute validations - $attributeValidations = $this->generateAttributeValidations($properties); + $attributeValidations = $this->generateAttributeValidationsFromDto($properties); if ($attributeValidations) { $lines[] = ' ->data->attributes->scoped(fn ($attributes) => $attributes'; $lines[] = $attributeValidations; @@ -157,19 +264,32 @@ protected function generateBodyValidation(Endpoint $endpoint): string } /** - * Generate attribute validation chain + * Generate attribute validation chain from DTO properties + * + * @param array $properties */ - protected function generateAttributeValidations(array $properties): string + protected function generateAttributeValidationsFromDto(array $properties): string { $lines = []; - // Limit to first 4 properties for the test - $propertiesToShow = array_slice($properties, 0, 4); + // Limit to first 4 properties for the test, skip timestamp fields + $count = 0; + foreach ($properties as $propInfo) { + if ($count >= 4) { + break; + } + + $propName = $propInfo['name']; + + // Skip read-only/auto-managed fields + if (in_array($propName, ['id', 'createdAt', 'updatedAt', 'deletedAt'])) { + continue; + } - foreach ($propertiesToShow as $propName => $propSpec) { - $value = $this->generateTestValueForProperty($propName, $propSpec); + $value = $this->generateTestValueForProperty($propName, $propInfo['type']); $assertionValue = $this->formatValueForAssertion($value); $lines[] = " ->{$propName}->toBe({$assertionValue})"; + $count++; } return implode("\n", $lines); diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index 8068a3a..b5051e5 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -4,17 +4,23 @@ namespace Timatic\SDK\Generator\TestGenerators; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\OpenApiSpecLoaderTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; class SingularGetRequestTestGenerator { use MockDataGeneratorTrait; - use OpenApiSpecLoaderTrait; use SchemaExtractorTrait; + protected ApiSpecification $specification; + + public function __construct(ApiSpecification $specification) + { + $this->specification = $specification; + } + /** * Check if endpoint is a singular GET request (GET with path parameters) */ diff --git a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php index 34098aa..7550ebe 100644 --- a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php +++ b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php @@ -4,6 +4,7 @@ namespace Timatic\SDK\Generator\TestGenerators\Traits; +use cebe\openapi\spec\Schema; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; trait MockDataGeneratorTrait @@ -13,50 +14,40 @@ trait MockDataGeneratorTrait */ protected function extractExampleFromSpec(Endpoint $endpoint): ?array { - $spec = $this->getOpenApiSpec(); - if (empty($spec)) { + $operation = $this->findOperationByEndpoint($endpoint); + if (! $operation) { return null; } - // Find the endpoint spec by operationId - $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); - if (! $endpointSpec) { + // Get the 200 response + $response = $operation->responses['200'] ?? $operation->responses[200] ?? null; + if (! $response) { + return null; + } + + // Get JSON content + $mediaType = $response->content['application/json'] ?? null; + if (! $mediaType) { return null; } // Try to find examples in this order: - // 1. Response-level example: responses.200.content.application/json.example - if (isset($endpointSpec['responses']['200']['content']['application/json']['example'])) { - return $endpointSpec['responses']['200']['content']['application/json']['example']; + // 1. MediaType-level example + if ($mediaType->example !== null) { + return $mediaType->example; } - // 2. Response-level examples array (use first one) - if (isset($endpointSpec['responses']['200']['content']['application/json']['examples'])) { - $examples = $endpointSpec['responses']['200']['content']['application/json']['examples']; - $firstExample = reset($examples); - if (isset($firstExample['value'])) { - return $firstExample['value']; + // 2. MediaType-level examples array (use first one) + if ($mediaType->examples && is_array($mediaType->examples)) { + $firstExample = reset($mediaType->examples); + if ($firstExample && isset($firstExample->value)) { + return $firstExample->value; } } // 3. Schema-level example - if (isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { - $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; - - // Check for direct example - if (isset($schema['example'])) { - return $schema['example']; - } - - // Check if schema references a component - if (isset($schema['$ref'])) { - $schema = $this->resolveSchemaReference($schema['$ref']); - - // Check for example in referenced schema - if (isset($schema['example'])) { - return $schema['example']; - } - } + if ($mediaType->schema && $mediaType->schema->example !== null) { + return $mediaType->schema->example; } return null; @@ -65,43 +56,36 @@ protected function extractExampleFromSpec(Endpoint $endpoint): ?array /** * Generate mock attributes based on schema properties */ - protected function generateMockAttributes(array $schema): array + protected function generateMockAttributes(Schema $schema): array { $attributes = []; - // Check if this is a JSON:API schema with attributes object - if (isset($schema['properties']['attributes']['properties'])) { - $properties = $schema['properties']['attributes']['properties']; - } elseif (isset($schema['properties'])) { - $properties = $schema['properties']; - } else { + // Extract the actual properties from the schema + $properties = $this->extractPropertiesFromSchema($schema); + + if (empty($properties)) { return ['name' => 'Mock value']; } - foreach ($properties as $propName => $propSpec) { - // Skip non-attribute fields - if (in_array($propName, ['id', 'type', 'attributes', 'relationships'])) { - continue; - } - - $attributes[$propName] = $this->getMockValueForProperty($propName, $propSpec); + foreach ($properties as $propName => $propSchema) { + $attributes[$propName] = $this->getMockValueForPropertySchema($propName, $propSchema); } return $attributes; } /** - * Generate a mock value based on property name and type + * Generate a mock value based on property name and Schema */ - protected function getMockValueForProperty(string $propertyName, array $propertySpec): mixed + protected function getMockValueForPropertySchema(string $propertyName, Schema $propertySchema): mixed { - // Check for example in property spec - if (isset($propertySpec['example'])) { - return $propertySpec['example']; + // Check for example in property schema + if ($propertySchema->example !== null) { + return $propertySchema->example; } - $type = $propertySpec['type'] ?? 'string'; - $format = $propertySpec['format'] ?? null; + $type = $propertySchema->type ?? 'string'; + $format = $propertySchema->format ?? null; // DateTime fields if ($format === 'date-time' || str_contains($propertyName, 'At') || str_contains($propertyName, 'Date')) { diff --git a/generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php b/generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php deleted file mode 100644 index 5adf296..0000000 --- a/generator/TestGenerators/Traits/OpenApiSpecLoaderTrait.php +++ /dev/null @@ -1,74 +0,0 @@ -openApiSpec === null) { - $specPath = __DIR__.'/../../../openapi.json'; - if (file_exists($specPath)) { - $this->openApiSpec = json_decode(file_get_contents($specPath), true); - } else { - $this->openApiSpec = []; - } - } - - return $this->openApiSpec; - } - - /** - * Find an endpoint specification by operation ID - */ - protected function findEndpointSpecByOperationId(string $operationId): ?array - { - $spec = $this->getOpenApiSpec(); - if (empty($spec) || ! isset($spec['paths'])) { - return null; - } - - // Search through all paths and methods to find the matching operationId - foreach ($spec['paths'] as $path => $pathItem) { - foreach ($pathItem as $method => $operation) { - // Skip non-operation keys (like parameters, summary, etc.) - if (! is_array($operation) || ! isset($operation['operationId'])) { - continue; - } - - if ($operation['operationId'] === $operationId) { - return $operation; - } - } - } - - return null; - } - - /** - * Resolve a schema reference ($ref) - */ - protected function resolveSchemaReference(string $ref): ?array - { - $spec = $this->getOpenApiSpec(); - $refPath = str_replace('#/', '', $ref); - $refParts = explode('/', $refPath); - - $referencedSchema = $spec; - foreach ($refParts as $part) { - if (! isset($referencedSchema[$part])) { - return null; - } - $referencedSchema = $referencedSchema[$part]; - } - - return $referencedSchema; - } -} diff --git a/generator/TestGenerators/Traits/SchemaExtractorTrait.php b/generator/TestGenerators/Traits/SchemaExtractorTrait.php index ac9acdd..9c12a2b 100644 --- a/generator/TestGenerators/Traits/SchemaExtractorTrait.php +++ b/generator/TestGenerators/Traits/SchemaExtractorTrait.php @@ -4,108 +4,205 @@ namespace Timatic\SDK\Generator\TestGenerators\Traits; +use cebe\openapi\spec\Operation; +use cebe\openapi\spec\Reference; +use cebe\openapi\spec\Schema; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; trait SchemaExtractorTrait { /** - * Get the request body schema for an endpoint from the OpenAPI spec + * The parsed ApiSpecification (must be provided by the class using this trait) */ - protected function getRequestSchemaForEndpoint(Endpoint $endpoint): ?array + protected ApiSpecification $specification; + + /** + * Get the response schema for an endpoint from the ApiSpecification + */ + protected function getResponseSchemaForEndpoint(Endpoint $endpoint): ?Schema { - $spec = $this->getOpenApiSpec(); - if (empty($spec)) { + $operation = $this->findOperationByEndpoint($endpoint); + if (! $operation) { + return null; + } + + // Get the 200 response schema + $response = $operation->responses['200'] ?? $operation->responses[200] ?? null; + if (! $response) { return null; } - // Find the endpoint spec by operationId - $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); - if (! $endpointSpec || ! isset($endpointSpec['requestBody']['content']['application/json']['schema'])) { + // Get the JSON response schema + $mediaType = $response->content['application/json'] ?? null; + if (! $mediaType || ! $mediaType->schema) { return null; } - $schema = $endpointSpec['requestBody']['content']['application/json']['schema']; + $schema = $this->resolveSchema($mediaType->schema); - // Resolve $ref if present - if (isset($schema['$ref'])) { - $schema = $this->resolveSchemaReference($schema['$ref']); + // Handle array responses (collections) - unwrap to get the item schema + if ($schema && $schema->type === 'array' && $schema->items) { + $schema = $this->resolveSchema($schema->items); } return $schema; } /** - * Get the response schema for an endpoint from the OpenAPI spec + * Get the request body schema for an endpoint from the ApiSpecification */ - protected function getResponseSchemaForEndpoint(Endpoint $endpoint): ?array + protected function getRequestSchemaForEndpoint(Endpoint $endpoint): ?Schema { - $spec = $this->getOpenApiSpec(); - if (empty($spec)) { + $operation = $this->findOperationByEndpoint($endpoint); + if (! $operation || ! $operation->requestBody) { return null; } - // Find the endpoint spec by operationId - $endpointSpec = $this->findEndpointSpecByOperationId($endpoint->name); - if (! $endpointSpec || ! isset($endpointSpec['responses']['200']['content']['application/json']['schema'])) { + // Get the JSON request schema + $mediaType = $operation->requestBody->content['application/json'] ?? null; + if (! $mediaType || ! $mediaType->schema) { return null; } - $schema = $endpointSpec['responses']['200']['content']['application/json']['schema']; + return $this->resolveSchema($mediaType->schema); + } + + /** + * Resolve a Schema or Reference to a Schema + */ + protected function resolveSchema(Schema|Reference|null $schemaOrRef): ?Schema + { + if ($schemaOrRef === null) { + return null; + } - // Handle array responses (collections) - if (isset($schema['type']) && $schema['type'] === 'array' && isset($schema['items'])) { - $schema = $schema['items']; + // If it's already a Schema, return it + if ($schemaOrRef instanceof Schema) { + return $schemaOrRef; } - // Resolve $ref if present - if (isset($schema['$ref'])) { - $schema = $this->resolveSchemaReference($schema['$ref']); + // If it's a Reference, resolve it + if ($schemaOrRef instanceof Reference) { + $resolved = $schemaOrRef->resolve(); + if ($resolved instanceof Schema) { + return $resolved; + } } - return $schema; + return null; + } + + /** + * Find an Operation in the ApiSpecification by matching endpoint operationId + */ + protected function findOperationByEndpoint(Endpoint $endpoint): ?Operation + { + if (! $this->specification->paths) { + return null; + } + + // Search through all paths to find matching operation + foreach ($this->specification->paths as $pathItem) { + $httpMethod = strtolower($endpoint->method->value); + + // Get the operation for this HTTP method + $operation = match ($httpMethod) { + 'get' => $pathItem->get, + 'post' => $pathItem->post, + 'patch' => $pathItem->patch, + 'delete' => $pathItem->delete, + 'put' => $pathItem->put, + default => null, + }; + + if (! $operation) { + continue; + } + + // Match by operationId + if ($operation->operationId === $endpoint->name) { + return $operation; + } + } + + return null; } /** * Extract properties from a schema (handling JSON:API structure) + * Returns an array of Schema objects keyed by property name + * + * @return array */ - protected function extractPropertiesFromSchema(array $schema): array + protected function extractPropertiesFromSchema(Schema $schema): array { + if (! $schema->properties) { + return []; + } + // For JSON:API, properties are nested in data.attributes - if (isset($schema['properties']['data']['properties']['attributes']['properties'])) { - return $schema['properties']['data']['properties']['attributes']['properties']; + if (isset($schema->properties['data'])) { + $dataSchema = $this->resolveSchema($schema->properties['data']); + if ($dataSchema && isset($dataSchema->properties['attributes'])) { + $attributesSchema = $this->resolveSchema($dataSchema->properties['attributes']); + if ($attributesSchema && $attributesSchema->properties) { + return $this->resolvePropertySchemas($attributesSchema->properties); + } + } } - // Fallback: check if properties has attributes - if (isset($schema['properties']['attributes']['properties'])) { - return $schema['properties']['attributes']['properties']; + // Fallback: check if properties has attributes directly + if (isset($schema->properties['attributes'])) { + $attributesSchema = $this->resolveSchema($schema->properties['attributes']); + if ($attributesSchema && $attributesSchema->properties) { + return $this->resolvePropertySchemas($attributesSchema->properties); + } } - // Direct properties - if (isset($schema['properties'])) { - $properties = $schema['properties']; - // Remove non-attribute fields - unset($properties['id'], $properties['type'], $properties['attributes'], $properties['relationships']); + // Direct properties - filter out JSON:API reserved fields + $properties = $schema->properties; + unset($properties['id'], $properties['type'], $properties['attributes'], $properties['relationships']); + + return $this->resolvePropertySchemas($properties); + } - return $properties; + /** + * Resolve all property schemas (convert References to Schemas) + * + * @param array $properties + * @return array + */ + protected function resolvePropertySchemas(array $properties): array + { + $resolved = []; + foreach ($properties as $name => $property) { + $schema = $this->resolveSchema($property); + if ($schema) { + $resolved[$name] = $schema; + } } - return []; + return $resolved; } /** * Get the resource type from a schema (e.g., "users", "entries") */ - protected function getResourceTypeFromSchema(array $schema): string + protected function getResourceTypeFromSchema(Schema $schema): string { - // Try to extract from schema title or description - if (isset($schema['title'])) { - return NameHelper::safeVariableName($schema['title']); + // Try to extract from schema title + if ($schema->title) { + return NameHelper::safeVariableName($schema->title); } - // Try to get from properties.type.example - if (isset($schema['properties']['type']['example'])) { - return $schema['properties']['type']['example']; + // Try to get from properties.type.example (JSON:API type field) + if (isset($schema->properties['type'])) { + $typeProperty = $schema->properties['type']; + if ($typeProperty instanceof Schema && $typeProperty->example) { + return $typeProperty->example; + } } // Fallback to generic name @@ -113,7 +210,7 @@ protected function getResourceTypeFromSchema(array $schema): string } /** - * Get the resource type for JSON:API (plural, lowercase) + * Get the resource type for JSON:API from endpoint (plural, lowercase) */ protected function getResourceTypeFromEndpoint(Endpoint $endpoint): string { diff --git a/generator/generate.php b/generator/generate.php index 914db48..a5e2572 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -74,7 +74,7 @@ dtoNamespaceSuffix: 'Dto', ); -// Create code generator with our custom JSON:API generators +// STEP 1: Generate SDK code (WITHOUT tests) echo "šŸ—ļø Generating SDK with JSON:API models...\n"; $generator = new CodeGenerator( config: $config, @@ -82,15 +82,11 @@ dtoGenerator: new JsonApiDtoGenerator($config), requestGenerator: new JsonApiRequestGenerator($config), resourceGenerator: new JsonApiResourceGenerator($config), - postProcessors: [new JsonApiPestTestGenerator] + postProcessors: [] // No test generator in first pass ); -// Generate the code $result = $generator->run($specification); -// Extract tests from result -$tests = $result->additionalFiles ?? null; - // Output directory $outputDir = __DIR__.'/../src'; @@ -200,9 +196,27 @@ function ($matches) { echo ' āœ“ '.basename($path)."\n"; } +// STEP 2: Dump autoload so DTOs are available for reflection +echo "\n"; +passthru('composer dump-autoload --quiet'); + +// STEP 3: Generate tests (NOW that DTOs exist on disk and can be reflected) +echo "\n🧪 Generating tests...\n"; +$testGenerator = new CodeGenerator( + config: $config, + connectorGenerator: new JsonApiConnectorGenerator($config), + dtoGenerator: new JsonApiDtoGenerator($config), + requestGenerator: new JsonApiRequestGenerator($config), + resourceGenerator: new JsonApiResourceGenerator($config), + postProcessors: [new JsonApiPestTestGenerator] +); + +$testResult = $testGenerator->run($specification); +$tests = $testResult->additionalFiles ?? null; + // Write test files (also apply path parameter transformations here as fallback) if ($tests && is_array($tests)) { - echo "\n🧪 Tests:\n"; + echo "🧪 Tests:\n"; foreach ($tests as $file) { if ($file instanceof \Crescat\SaloonSdkGenerator\Data\TaggedOutputFile) { $testPath = __DIR__.'/../'.$file->path; diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index 62cb49c..016a2d3 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -16,7 +16,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Approve; - $dto->name = 'test value'; + $dto->entryId = 'mock-id-123'; + $dto->overtimeTypeId = 'mock-id-123'; + $dto->startedAt = 'test value'; + $dto->endedAt = 'test value'; // todo: add every other DTO field $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', $dto); @@ -28,7 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('approve') ->data->attributes->scoped(fn ($attributes) => $attributes - ->name->toBe('test value') + ->entryId->toBe('mock-id-123') + ->overtimeTypeId->toBe('mock-id-123') + ->startedAt->toBe('test value') + ->endedAt->toBe('test value') ); return true; diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index f586503..2e92fa3 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -65,10 +65,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Budget; - $dto->budgetTypeId = 'test-id-123'; - $dto->customerId = 'test-id-123'; - $dto->showToCustomer = false; - $dto->changeId = 'test-id-123'; + $dto->budgetTypeId = 'mock-id-123'; + $dto->customerId = 'mock-id-123'; + $dto->showToCustomer = true; + $dto->changeId = 'mock-id-123'; // todo: add every other DTO field $this->timaticConnector->budget()->postBudgets($dto); @@ -80,10 +80,10 @@ // POST calls dont have an ID field ->data->type->toBe('budget') ->data->attributes->scoped(fn ($attributes) => $attributes - ->budgetTypeId->toBe('test-id-123') - ->customerId->toBe('test-id-123') - ->showToCustomer->toBe(false) - ->changeId->toBe('test-id-123') + ->budgetTypeId->toBe('mock-id-123') + ->customerId->toBe('mock-id-123') + ->showToCustomer->toBe(true) + ->changeId->toBe('mock-id-123') ); return true; @@ -97,7 +97,7 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'data' => 'Mock value', + 'name' => 'Mock value', ], ], ], 200), @@ -133,10 +133,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Budget; - $dto->budgetTypeId = 'test-id-123'; - $dto->customerId = 'test-id-123'; - $dto->showToCustomer = false; - $dto->changeId = 'test-id-123'; + $dto->budgetTypeId = 'mock-id-123'; + $dto->customerId = 'mock-id-123'; + $dto->showToCustomer = true; + $dto->changeId = 'mock-id-123'; // todo: add every other DTO field $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', $dto); @@ -147,10 +147,10 @@ ->toHaveKey('data') ->data->type->toBe('budget') ->data->attributes->scoped(fn ($attributes) => $attributes - ->budgetTypeId->toBe('test-id-123') - ->customerId->toBe('test-id-123') - ->showToCustomer->toBe(false) - ->changeId->toBe('test-id-123') + ->budgetTypeId->toBe('mock-id-123') + ->customerId->toBe('mock-id-123') + ->showToCustomer->toBe(true) + ->changeId->toBe('mock-id-123') ); return true; diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index 76677ed..1d07c53 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -1,15 +1,19 @@ timaticConnector = new Timatic\SDK\TimaticConnector; + $this->timaticConnector = new Timatic\SDK\TimaticConnector( + + ); }); + it('calls the postCorrections method in the Correction resource', function () { $mockClient = Saloon::fake([ PostCorrectionsRequest::class => MockResponse::make([], 200), @@ -17,8 +21,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Correction; - $dto->createdAt = '2025-01-01T10:00:00Z'; - $dto->updatedAt = '2025-01-01T10:00:00Z'; + $dto->name = 'test value'; // todo: add every other DTO field $this->timaticConnector->correction()->postCorrections($dto); @@ -29,15 +32,12 @@ ->toHaveKey('data') // POST calls dont have an ID field ->data->type->toBe('correction') - ->data->attributes->scoped(fn ($attributes) => $attributes - ->createdAt->toBe('2025-01-01T10:00:00Z') - ->updatedAt->toBe('2025-01-01T10:00:00Z') - ); return true; }); }); + it('calls the patchCorrection method in the Correction resource', function () { $mockClient = Saloon::fake([ PatchCorrectionRequest::class => MockResponse::make([], 200), @@ -45,8 +45,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Correction; - $dto->createdAt = '2025-01-01T10:00:00Z'; - $dto->updatedAt = '2025-01-01T10:00:00Z'; + $dto->name = 'test value'; // todo: add every other DTO field $this->timaticConnector->correction()->patchCorrection(correctionId: 'test string', $dto); @@ -56,10 +55,6 @@ expect($request->body()->all()) ->toHaveKey('data') ->data->type->toBe('correction') - ->data->attributes->scoped(fn ($attributes) => $attributes - ->createdAt->toBe('2025-01-01T10:00:00Z') - ->updatedAt->toBe('2025-01-01T10:00:00Z') - ); return true; }); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index 8c61649..a2f4f55 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -61,10 +61,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Customer; - $dto->externalId = 'test-id-123'; - $dto->name = 'test name'; + $dto->externalId = 'mock-id-123'; + $dto->name = 'test value'; $dto->hourlyRate = 'test value'; - $dto->accountManagerUserId = 'test-id-123'; + $dto->accountManagerUserId = 'mock-id-123'; // todo: add every other DTO field $this->timaticConnector->customer()->postCustomers($dto); @@ -76,10 +76,10 @@ // POST calls dont have an ID field ->data->type->toBe('customer') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('test-id-123') - ->name->toBe('test name') + ->externalId->toBe('mock-id-123') + ->name->toBe('test value') ->hourlyRate->toBe('test value') - ->accountManagerUserId->toBe('test-id-123') + ->accountManagerUserId->toBe('mock-id-123') ); return true; @@ -93,7 +93,7 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'data' => 'Mock value', + 'name' => 'Mock value', ], ], ], 200), @@ -129,10 +129,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Customer; - $dto->externalId = 'test-id-123'; - $dto->name = 'test name'; + $dto->externalId = 'mock-id-123'; + $dto->name = 'test value'; $dto->hourlyRate = 'test value'; - $dto->accountManagerUserId = 'test-id-123'; + $dto->accountManagerUserId = 'mock-id-123'; // todo: add every other DTO field $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', $dto); @@ -143,10 +143,10 @@ ->toHaveKey('data') ->data->type->toBe('customer') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('test-id-123') - ->name->toBe('test name') + ->externalId->toBe('mock-id-123') + ->name->toBe('test value') ->hourlyRate->toBe('test value') - ->accountManagerUserId->toBe('test-id-123') + ->accountManagerUserId->toBe('mock-id-123') ); return true; diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index 6fe3537..b1e633a 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -59,7 +59,7 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'data' => 'Mock value', + 'name' => 'Mock value', ], ], ], 200), diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 28e33b9..8301167 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -65,7 +65,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Entry; - $dto->ticketId = 'test-id-123'; + $dto->ticketId = 'mock-id-123'; $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; @@ -80,7 +80,7 @@ // POST calls dont have an ID field ->data->type->toBe('entry') ->data->attributes->scoped(fn ($attributes) => $attributes - ->ticketId->toBe('test-id-123') + ->ticketId->toBe('mock-id-123') ->ticketNumber->toBe('test value') ->ticketTitle->toBe('test value') ->ticketType->toBe('test value') @@ -97,7 +97,7 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'data' => 'Mock value', + 'name' => 'Mock value', ], ], ], 200), @@ -133,7 +133,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Entry; - $dto->ticketId = 'test-id-123'; + $dto->ticketId = 'mock-id-123'; $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; @@ -147,7 +147,7 @@ ->toHaveKey('data') ->data->type->toBe('entry') ->data->attributes->scoped(fn ($attributes) => $attributes - ->ticketId->toBe('test-id-123') + ->ticketId->toBe('mock-id-123') ->ticketNumber->toBe('test value') ->ticketTitle->toBe('test value') ->ticketType->toBe('test value') diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index 68f82c8..0c1a444 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -16,10 +16,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Event; - $dto->userId = 'test-id-123'; - $dto->budgetId = 'test-id-123'; - $dto->ticketId = 'test-id-123'; - $dto->sourceId = 'test-id-123'; + $dto->userId = 'mock-id-123'; + $dto->budgetId = 'mock-id-123'; + $dto->ticketId = 'mock-id-123'; + $dto->sourceId = 'mock-id-123'; // todo: add every other DTO field $this->timaticConnector->event()->postEvents($dto); @@ -31,10 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('event') ->data->attributes->scoped(fn ($attributes) => $attributes - ->userId->toBe('test-id-123') - ->budgetId->toBe('test-id-123') - ->ticketId->toBe('test-id-123') - ->sourceId->toBe('test-id-123') + ->userId->toBe('mock-id-123') + ->budgetId->toBe('mock-id-123') + ->ticketId->toBe('mock-id-123') + ->sourceId->toBe('mock-id-123') ); return true; diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index f29f094..2d5eb41 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -16,7 +16,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\MarkAsExported; - $dto->name = 'test value'; + $dto->entryId = 'mock-id-123'; + $dto->overtimeTypeId = 'mock-id-123'; + $dto->startedAt = 'test value'; + $dto->endedAt = 'test value'; // todo: add every other DTO field $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', $dto); @@ -28,7 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('markAsExported') ->data->attributes->scoped(fn ($attributes) => $attributes - ->name->toBe('test value') + ->entryId->toBe('mock-id-123') + ->overtimeTypeId->toBe('mock-id-123') + ->startedAt->toBe('test value') + ->endedAt->toBe('test value') ); return true; diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index d47a298..2aaa277 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -16,7 +16,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\MarkAsInvoiced; - $dto->name = 'test value'; + $dto->ticketId = 'mock-id-123'; + $dto->ticketNumber = 'test value'; + $dto->ticketTitle = 'test value'; + $dto->ticketType = 'test value'; // todo: add every other DTO field $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced(entryId: 'test string', $dto); @@ -28,7 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('markAsInvoiced') ->data->attributes->scoped(fn ($attributes) => $attributes - ->name->toBe('test value') + ->ticketId->toBe('mock-id-123') + ->ticketNumber->toBe('test value') + ->ticketTitle->toBe('test value') + ->ticketType->toBe('test value') ); return true; diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index a43e95b..75ac031 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -51,10 +51,8 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Team; - $dto->externalId = 'test-id-123'; - $dto->name = 'test name'; - $dto->createdAt = '2025-01-01T10:00:00Z'; - $dto->updatedAt = '2025-01-01T10:00:00Z'; + $dto->externalId = 'mock-id-123'; + $dto->name = 'test value'; // todo: add every other DTO field $this->timaticConnector->team()->postTeams($dto); @@ -66,10 +64,8 @@ // POST calls dont have an ID field ->data->type->toBe('team') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('test-id-123') - ->name->toBe('test name') - ->createdAt->toBe('2025-01-01T10:00:00Z') - ->updatedAt->toBe('2025-01-01T10:00:00Z') + ->externalId->toBe('mock-id-123') + ->name->toBe('test value') ); return true; @@ -83,7 +79,7 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'data' => 'Mock value', + 'name' => 'Mock value', ], ], ], 200), @@ -119,10 +115,8 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Team; - $dto->externalId = 'test-id-123'; - $dto->name = 'test name'; - $dto->createdAt = '2025-01-01T10:00:00Z'; - $dto->updatedAt = '2025-01-01T10:00:00Z'; + $dto->externalId = 'mock-id-123'; + $dto->name = 'test value'; // todo: add every other DTO field $this->timaticConnector->team()->patchTeam(teamId: 'test string', $dto); @@ -133,10 +127,8 @@ ->toHaveKey('data') ->data->type->toBe('team') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('test-id-123') - ->name->toBe('test name') - ->createdAt->toBe('2025-01-01T10:00:00Z') - ->updatedAt->toBe('2025-01-01T10:00:00Z') + ->externalId->toBe('mock-id-123') + ->name->toBe('test value') ); return true; diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index d625b14..d72b2fb 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -61,8 +61,8 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\User; - $dto->externalId = 'test-id-123'; - $dto->email = 'test value'; + $dto->externalId = 'mock-id-123'; + $dto->email = 'test@example.com'; // todo: add every other DTO field $this->timaticConnector->user()->postUsers($dto); @@ -74,8 +74,8 @@ // POST calls dont have an ID field ->data->type->toBe('user') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('test-id-123') - ->email->toBe('test value') + ->externalId->toBe('mock-id-123') + ->email->toBe('test@example.com') ); return true; @@ -125,8 +125,8 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\User; - $dto->externalId = 'test-id-123'; - $dto->email = 'test value'; + $dto->externalId = 'mock-id-123'; + $dto->email = 'test@example.com'; // todo: add every other DTO field $this->timaticConnector->user()->patchUser(userId: 'test string', $dto); @@ -137,8 +137,8 @@ ->toHaveKey('data') ->data->type->toBe('user') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('test-id-123') - ->email->toBe('test value') + ->externalId->toBe('mock-id-123') + ->email->toBe('test@example.com') ); return true; From 6ee301a6ed760d15d70e09c58a808f0737a8d1b3 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 11:55:25 +0100 Subject: [PATCH 35/62] hydration wip --- generator/JsonApiDtoGenerator.php | 4 +- src/Concerns/HasAttributes.php | 2 +- src/Concerns/Model.php | 2 +- src/Dto/Approve.php | 2 +- src/Dto/Budget.php | 4 +- src/Dto/BudgetTimeSpentTotal.php | 4 +- src/Dto/BudgetType.php | 2 +- src/Dto/Correction.php | 4 +- src/Dto/Customer.php | 4 +- src/Dto/DailyProgress.php | 4 +- src/Dto/Entry.php | 2 +- src/Dto/EntrySuggestion.php | 2 +- src/Dto/Event.php | 2 +- src/Dto/MarkAsExported.php | 2 +- src/Dto/MarkAsInvoiced.php | 2 +- src/Dto/Overtime.php | 2 +- src/Dto/Team.php | 4 +- src/Dto/TimeSpentTotal.php | 4 +- src/Dto/User.php | 2 +- src/Dto/UserCustomerHoursAggregate.php | 2 +- src/{ => Hydration}/Attributes/DateTime.php | 2 +- src/{ => Hydration}/Attributes/Property.php | 2 +- src/Hydration/Attributes/Relationship.php | 18 ++ src/Hydration/Facades/Hydrator.php | 21 +++ src/Hydration/Hydrator.php | 188 ++++++++++++++++++++ src/Hydration/RelationType.php | 11 ++ 26 files changed, 268 insertions(+), 30 deletions(-) rename src/{ => Hydration}/Attributes/DateTime.php (81%) rename src/{ => Hydration}/Attributes/Property.php (81%) create mode 100644 src/Hydration/Attributes/Relationship.php create mode 100644 src/Hydration/Facades/Hydrator.php create mode 100644 src/Hydration/Hydrator.php create mode 100644 src/Hydration/RelationType.php diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php index 8dcd145..7b57ece 100644 --- a/generator/JsonApiDtoGenerator.php +++ b/generator/JsonApiDtoGenerator.php @@ -13,9 +13,9 @@ use Illuminate\Support\Str; use Nette\PhpGenerator\ClassType; use Nette\PhpGenerator\PhpFile; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class JsonApiDtoGenerator extends Generator { diff --git a/src/Concerns/HasAttributes.php b/src/Concerns/HasAttributes.php index 468c98b..858eb2b 100644 --- a/src/Concerns/HasAttributes.php +++ b/src/Concerns/HasAttributes.php @@ -5,7 +5,7 @@ namespace Timatic\SDK\Concerns; use ReflectionClass; -use Timatic\SDK\Attributes\Property; +use Timatic\SDK\Hydration\Attributes\Property; trait HasAttributes { diff --git a/src/Concerns/Model.php b/src/Concerns/Model.php index 0bf8815..c283056 100644 --- a/src/Concerns/Model.php +++ b/src/Concerns/Model.php @@ -6,7 +6,7 @@ use Illuminate\Support\Str; use ReflectionClass; -use Timatic\SDK\Attributes\Property; +use Timatic\SDK\Hydration\Attributes\Property; abstract class Model implements ModelInterface { diff --git a/src/Dto/Approve.php b/src/Dto/Approve.php index d2751f8..ff3432e 100644 --- a/src/Dto/Approve.php +++ b/src/Dto/Approve.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class Approve extends Model { diff --git a/src/Dto/Budget.php b/src/Dto/Budget.php index 1813721..3c1f311 100644 --- a/src/Dto/Budget.php +++ b/src/Dto/Budget.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class Budget extends Model { diff --git a/src/Dto/BudgetTimeSpentTotal.php b/src/Dto/BudgetTimeSpentTotal.php index a274c30..3c263dd 100644 --- a/src/Dto/BudgetTimeSpentTotal.php +++ b/src/Dto/BudgetTimeSpentTotal.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class BudgetTimeSpentTotal extends Model { diff --git a/src/Dto/BudgetType.php b/src/Dto/BudgetType.php index b028577..dee6c34 100644 --- a/src/Dto/BudgetType.php +++ b/src/Dto/BudgetType.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class BudgetType extends Model { diff --git a/src/Dto/Correction.php b/src/Dto/Correction.php index c4d8cc7..c43d133 100644 --- a/src/Dto/Correction.php +++ b/src/Dto/Correction.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class Correction extends Model { diff --git a/src/Dto/Customer.php b/src/Dto/Customer.php index 87a1d1d..db2ecae 100644 --- a/src/Dto/Customer.php +++ b/src/Dto/Customer.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class Customer extends Model { diff --git a/src/Dto/DailyProgress.php b/src/Dto/DailyProgress.php index d9015a4..506e2fa 100644 --- a/src/Dto/DailyProgress.php +++ b/src/Dto/DailyProgress.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class DailyProgress extends Model { diff --git a/src/Dto/Entry.php b/src/Dto/Entry.php index c1d17de..5b56aba 100644 --- a/src/Dto/Entry.php +++ b/src/Dto/Entry.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class Entry extends Model { diff --git a/src/Dto/EntrySuggestion.php b/src/Dto/EntrySuggestion.php index 8b79dfa..1b21f32 100644 --- a/src/Dto/EntrySuggestion.php +++ b/src/Dto/EntrySuggestion.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class EntrySuggestion extends Model { diff --git a/src/Dto/Event.php b/src/Dto/Event.php index 43dbca1..08d6111 100644 --- a/src/Dto/Event.php +++ b/src/Dto/Event.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class Event extends Model { diff --git a/src/Dto/MarkAsExported.php b/src/Dto/MarkAsExported.php index 48d8b2d..3098091 100644 --- a/src/Dto/MarkAsExported.php +++ b/src/Dto/MarkAsExported.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class MarkAsExported extends Model { diff --git a/src/Dto/MarkAsInvoiced.php b/src/Dto/MarkAsInvoiced.php index 12b1771..4ea73d9 100644 --- a/src/Dto/MarkAsInvoiced.php +++ b/src/Dto/MarkAsInvoiced.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class MarkAsInvoiced extends Model { diff --git a/src/Dto/Overtime.php b/src/Dto/Overtime.php index 63bdc1c..6e33ba7 100644 --- a/src/Dto/Overtime.php +++ b/src/Dto/Overtime.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class Overtime extends Model { diff --git a/src/Dto/Team.php b/src/Dto/Team.php index 1ae0b0e..a3c48a5 100644 --- a/src/Dto/Team.php +++ b/src/Dto/Team.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class Team extends Model { diff --git a/src/Dto/TimeSpentTotal.php b/src/Dto/TimeSpentTotal.php index 62ab77d..23bca7a 100644 --- a/src/Dto/TimeSpentTotal.php +++ b/src/Dto/TimeSpentTotal.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\DateTime; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; +use Timatic\SDK\Hydration\Attributes\Property; class TimeSpentTotal extends Model { diff --git a/src/Dto/User.php b/src/Dto/User.php index 6d4372e..31a9b6b 100644 --- a/src/Dto/User.php +++ b/src/Dto/User.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class User extends Model { diff --git a/src/Dto/UserCustomerHoursAggregate.php b/src/Dto/UserCustomerHoursAggregate.php index 210468c..c87309f 100644 --- a/src/Dto/UserCustomerHoursAggregate.php +++ b/src/Dto/UserCustomerHoursAggregate.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Attributes\Property; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\Property; class UserCustomerHoursAggregate extends Model { diff --git a/src/Attributes/DateTime.php b/src/Hydration/Attributes/DateTime.php similarity index 81% rename from src/Attributes/DateTime.php rename to src/Hydration/Attributes/DateTime.php index 17ea6b9..2b5b2b0 100644 --- a/src/Attributes/DateTime.php +++ b/src/Hydration/Attributes/DateTime.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Attributes; +namespace Timatic\SDK\Hydration\Attributes; use Attribute; diff --git a/src/Attributes/Property.php b/src/Hydration/Attributes/Property.php similarity index 81% rename from src/Attributes/Property.php rename to src/Hydration/Attributes/Property.php index c748868..b6a85ee 100644 --- a/src/Attributes/Property.php +++ b/src/Hydration/Attributes/Property.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Attributes; +namespace Timatic\SDK\Hydration\Attributes; use Attribute; diff --git a/src/Hydration/Attributes/Relationship.php b/src/Hydration/Attributes/Relationship.php new file mode 100644 index 0000000..fae0333 --- /dev/null +++ b/src/Hydration/Attributes/Relationship.php @@ -0,0 +1,18 @@ + $model + */ + public function __construct(public string $model, public RelationType $type) {} +} diff --git a/src/Hydration/Facades/Hydrator.php b/src/Hydration/Facades/Hydrator.php new file mode 100644 index 0000000..927411d --- /dev/null +++ b/src/Hydration/Facades/Hydrator.php @@ -0,0 +1,21 @@ + hydrateCollection(string $model, array $data, array|null $included = null) + * @method static Model hydrate(string $model, array $item, array|null $included = null) + */ +class Hydrator extends Facade +{ + protected static function getFacadeAccessor() + { + return \Timatic\SDK\Hydration\Hydrator::class; + } +} diff --git a/src/Hydration/Hydrator.php b/src/Hydration/Hydrator.php new file mode 100644 index 0000000..68dc4e7 --- /dev/null +++ b/src/Hydration/Hydrator.php @@ -0,0 +1,188 @@ +|Model $model + * @param array $item + * @param array|null $included + * + * @throws ReflectionException + */ + public function hydrate(string|Model $model, array $item, ?array $included = null): Model + { + if (is_null($included)) { + $included = []; + } + + $included = Arr::keyBy($included, fn ($includedItem) => $includedItem['id'].'-'.$includedItem['type']); + + if (is_string($model)) { + $model = $this->getModelFromClassName($model); + } + + $reflectionClass = new ReflectionClass($model); + + $fillable = Arr::pluck($this->filterProperties($reflectionClass, Property::class), 'name'); + + $hydrator = function (Model $model, string $property, mixed $value) use ($fillable, $reflectionClass) { + if (in_array($property, $fillable)) { + $type = $reflectionClass->getProperty($property)->getType(); + + $propertyReflectionAttributes = $reflectionClass->getProperty($property)->getAttributes(Property::class); + Assert::count($propertyReflectionAttributes, 1); + + $propertyArguments = $propertyReflectionAttributes[0]->getArguments(); + if (isset($propertyArguments['hydrator'])) { + Assert::isCallable($propertyArguments['hydrator']); + + $value = $propertyArguments['hydrator']($value); + } + + $dateTimeAttributes = $reflectionClass->getProperty($property)->getAttributes(DateTime::class); + if ($dateTimeAttributes !== []) { + + if ($value === null && $reflectionClass->getProperty($property)->getType()?->allowsNull()) { + $value = null; + } else { + $value = Carbon::parse($value); + } + } + + Assert::isInstanceOf($type, ReflectionNamedType::class); + + $model->$property = $value; + } + }; + + $hydrator($model, 'id', $item['id']); + + foreach ($item['attributes'] as $attribute => $value) { + $hydrator($model, $attribute, $value); + } + + $this->hydrateRelations($reflectionClass, $item, $included, $model); + + return $model; + } + + /** + * @param class-string $model + * @param array $data + * @param array|null $included + * @return Collection + */ + public function hydrateCollection(string $model, array $data, ?array $included = null): Collection + { + $collection = new Collection; + + foreach ($data as $item) { + $collection->add($this->hydrate($model, $item, $included)); + } + + return $collection; + } + + /** + * @param ReflectionClass $reflectionClass + * @return array + */ + private function filterProperties(ReflectionClass $reflectionClass, string ...$attributes): array + { + return array_values(array_filter( + $reflectionClass->getProperties(), + function (ReflectionProperty $value) use ($attributes) { + $hasAttributes = true; + + foreach ($attributes as $attribute) { + $hasAttributes = (bool) count($value->getAttributes($attribute)); + } + + return $hasAttributes; + } + )); + } + + /** + * @param ReflectionClass $reflectionClass + * @param array $item + * @param array $included + */ + protected function hydrateRelations(ReflectionClass $reflectionClass, array $item, array $included, Model $model): void + { + $relationProperties = Arr::keyBy($this->filterProperties($reflectionClass, Relationship::class), 'name'); + + if (! array_key_exists('relationships', $item)) { + return; + } + + foreach ($item['relationships'] as $relationshipName => $relationship) { + if ( + ! array_key_exists($relationshipName, $relationProperties) + || ! array_key_exists('data', $relationship) + || $relationship['data'] === null + ) { + continue; + } + + /** @var Relationship $relationAttribute */ + $relationAttribute = $relationProperties[$relationshipName] + ->getAttributes(Relationship::class)[0]->newInstance(); + + $relationModel = $relationAttribute->model; + + if ($relationAttribute->type === RelationType::Many) { + $hydratedRelation = new Collection; + + foreach ($relationship['data'] as $relationItem) { + $includedItem = $included[$relationItem['id'].'-'.$relationItem['type']] ?? null; + + if (! is_null($includedItem)) { + $hydratedRelation->push( + $this->hydrate($relationModel, $includedItem, $included) + ); + } + } + + $model->{$relationshipName} = $hydratedRelation; + } elseif ($relationAttribute->type === RelationType::One) { + $relationItem = $relationship['data']; + $includedItem = $included[$relationItem['id'].'-'.$relationItem['type']] ?? null; + + $model->{$relationshipName} = $this->hydrate($relationModel, $includedItem, $included); + } + } + } + + /** + * @param class-string $model + */ + private function getModelFromClassName(string $model): Model + { + $reflectionClass = new ReflectionClass($model); + if ($reflectionClass->getConstructor()?->getNumberOfRequiredParameters() > 0) { + return $reflectionClass->newInstanceWithoutConstructor(); + } + + return new $model; + } +} diff --git a/src/Hydration/RelationType.php b/src/Hydration/RelationType.php new file mode 100644 index 0000000..7f2c55e --- /dev/null +++ b/src/Hydration/RelationType.php @@ -0,0 +1,11 @@ + Date: Sat, 22 Nov 2025 13:51:29 +0100 Subject: [PATCH 36/62] remove redundant customizations --- generator/generate.php | 78 +----------------------------------------- 1 file changed, 1 insertion(+), 77 deletions(-) diff --git a/generator/generate.php b/generator/generate.php index a5e2572..09fa508 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -117,67 +117,6 @@ function writeFile($file, $outputDir, $namespace) // Post-process and write resources echo "\nšŸ“¦ Resources:\n"; foreach ($result->resourceClasses as $resourceClass) { - $namespace = array_values($resourceClass->getNamespaces())[0]; - $classType = array_values($namespace->getClasses())[0]; - - // Fix imports - add "Request" suffix to all Request class imports - foreach ($namespace->getUses() as $alias => $fqn) { - // Check if this is a Request class import - if (str_contains($fqn, '\\Requests\\')) { - $className = basename(str_replace('\\', '/', $fqn)); - // Add "Request" suffix if not already present - if (! str_ends_with($className, 'Request')) { - $newFqn = substr($fqn, 0, -strlen($className)).$className.'Request'; - $namespace->removeUse($fqn); - $namespace->addUse($newFqn, $className.'Request'); - } - } - } - - // Fix class references: add "Request" suffix to class instantiations in method body - foreach ($classType->getMethods() as $method) { - $body = $method->getBody(); - - // Add "Request" suffix to class instantiations in method body - $body = preg_replace_callback( - '/\(new\s+(\w+)\(/', - function ($matches) { - $className = $matches[1]; - // Add "Request" suffix if not already present - if (! str_ends_with($className, 'Request')) { - return '(new '.$className.'Request('; - } - - return $matches[0]; - }, - $body - ); - - // Fix mutation methods that have $data parameter but don't pass it - $methodName = $method->getName(); - if (preg_match('/^(post|patch)/i', $methodName) && $method->hasParameter('data')) { - // Check if Request instantiation exists without $data parameter - // Matches: new PostBudgetsRequest() or new PostBudgetsRequest (without parens) - if (preg_match('/new\s+\w+Request\(\s*\)/', $body)) { - // Has empty parens: replace () with ($data) - $body = preg_replace( - '/new\s+(\w+Request)\(\s*\)/', - 'new $1($data)', - $body - ); - } elseif (preg_match('/new\s+\w+Request(?![(\w])/', $body)) { - // No parens: add ($data) - $body = preg_replace( - '/new\s+(\w+Request)(?![(\w])/', - 'new $1($data)', - $body - ); - } - } - - $method->setBody($body); - } - $path = writeFile($resourceClass, $outputDir, $config->namespace); echo ' āœ“ '.basename($path)."\n"; } @@ -227,22 +166,7 @@ function ($matches) { mkdir($dir, 0755, true); } - // Apply path parameter transformations (as fallback if generator didn't do it) - $testContent = $file->file; - $resourceNames = [ - 'budget', 'customer', 'user', 'team', 'entry', 'entrySuggestion', - 'correction', 'change', 'incident', 'overtime', - ]; - - foreach ($resourceNames as $resourceName) { - $testContent = preg_replace( - "/\b{$resourceName}:\s*(['\"])/", - "{$resourceName}Id: $1", - $testContent - ); - } - - file_put_contents($testPath, $testContent); + file_put_contents($testPath, $file->file); echo ' āœ“ '.basename($testPath)."\n"; } } From e467e8244a336c59da9d47c2982405724e4d365e Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 15:32:07 +0100 Subject: [PATCH 37/62] use generatedCode for DTOs tests --- generator/JsonApiPestTestGenerator.php | 12 ++-- .../CollectionRequestTestGenerator.php | 6 +- .../DeleteRequestTestGenerator.php | 6 +- .../MutationRequestTestGenerator.php | 71 +++++++++++-------- .../SingularGetRequestTestGenerator.php | 6 +- generator/generate.php | 36 +++------- tests/Requests/CorrectionTest.php | 15 ++-- 7 files changed, 78 insertions(+), 74 deletions(-) diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 2f93c1c..2190925 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -28,18 +28,18 @@ class JsonApiPestTestGenerator extends PestTestGenerator protected DeleteRequestTestGenerator $deleteTestGenerator; /** - * Override process() to instantiate test generators with ApiSpecification + * Override process() to instantiate test generators with ApiSpecification and GeneratedCode */ public function process( Config $config, ApiSpecification $specification, GeneratedCode $generatedCode, ): PhpFile|array|null { - // Instantiate test generators with the parsed ApiSpecification - $this->collectionTestGenerator = new CollectionRequestTestGenerator($specification); - $this->mutationTestGenerator = new MutationRequestTestGenerator($specification); - $this->singularGetTestGenerator = new SingularGetRequestTestGenerator($specification); - $this->deleteTestGenerator = new DeleteRequestTestGenerator($specification); + // Instantiate test generators with the parsed ApiSpecification and GeneratedCode + $this->collectionTestGenerator = new CollectionRequestTestGenerator($specification, $generatedCode); + $this->mutationTestGenerator = new MutationRequestTestGenerator($specification, $generatedCode); + $this->singularGetTestGenerator = new SingularGetRequestTestGenerator($specification, $generatedCode); + $this->deleteTestGenerator = new DeleteRequestTestGenerator($specification, $generatedCode); // Call parent to continue normal processing return parent::process($config, $specification, $generatedCode); diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 4c73414..88b387b 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -6,6 +6,7 @@ use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; @@ -19,9 +20,12 @@ class CollectionRequestTestGenerator protected ApiSpecification $specification; - public function __construct(ApiSpecification $specification) + protected GeneratedCode $generatedCode; + + public function __construct(ApiSpecification $specification, GeneratedCode $generatedCode) { $this->specification = $specification; + $this->generatedCode = $generatedCode; } /** diff --git a/generator/TestGenerators/DeleteRequestTestGenerator.php b/generator/TestGenerators/DeleteRequestTestGenerator.php index b8724ad..b60e791 100644 --- a/generator/TestGenerators/DeleteRequestTestGenerator.php +++ b/generator/TestGenerators/DeleteRequestTestGenerator.php @@ -6,14 +6,18 @@ use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; class DeleteRequestTestGenerator { protected ApiSpecification $specification; - public function __construct(ApiSpecification $specification) + protected GeneratedCode $generatedCode; + + public function __construct(ApiSpecification $specification, GeneratedCode $generatedCode) { $this->specification = $specification; + $this->generatedCode = $generatedCode; } /** diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 006814f..4212c78 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -7,6 +7,7 @@ use cebe\openapi\spec\Schema; use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; @@ -18,9 +19,12 @@ class MutationRequestTestGenerator protected ApiSpecification $specification; - public function __construct(ApiSpecification $specification) + protected GeneratedCode $generatedCode; + + public function __construct(ApiSpecification $specification, GeneratedCode $generatedCode) { $this->specification = $specification; + $this->generatedCode = $generatedCode; } /** @@ -104,7 +108,7 @@ protected function generateDtoInstantiation(Endpoint $endpoint): string protected function generateDtoProperties(Endpoint $endpoint): string { $dtoClassName = $this->getDtoClassName($endpoint); - $properties = $this->getDtoPropertiesViaReflection($dtoClassName); + $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); if (empty($properties)) { return " \$dto->name = 'test value';"; @@ -140,45 +144,50 @@ protected function generateDtoProperties(Endpoint $endpoint): string } /** - * Get DTO properties via PHP reflection (DTO classes now exist on disk) + * Get DTO properties from generated code (PhpFile objects) * * @return array */ - protected function getDtoPropertiesViaReflection(string $dtoClassName): array + protected function getDtoPropertiesFromGeneratedCode(string $dtoClassName): array { - $fullClassName = "\\Timatic\\SDK\\Dto\\{$dtoClassName}"; - - // Check if class exists (it should, because we dumped autoload) - if (! class_exists($fullClassName)) { + // Check if DTO exists in generated code + if (! isset($this->generatedCode->dtoClasses[$dtoClassName])) { return []; } + $phpFile = $this->generatedCode->dtoClasses[$dtoClassName]; $properties = []; - try { - $reflection = new \ReflectionClass($fullClassName); - $reflectionProperties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC); + // Get the first namespace in the file + $namespace = array_values($phpFile->getNamespaces())[0] ?? null; + if (! $namespace) { + return []; + } - foreach ($reflectionProperties as $property) { - // Skip static properties - if ($property->isStatic()) { - continue; - } + // Get the first class in the namespace + $classType = array_values($namespace->getClasses())[0] ?? null; + if (! $classType) { + return []; + } - $type = $property->getType(); - $typeName = null; + // Extract properties from the class + foreach ($classType->getProperties() as $property) { + // Skip static properties + if ($property->isStatic()) { + continue; + } - if ($type instanceof \ReflectionNamedType) { - $typeName = ($type->allowsNull() ? '?' : '').$type->getName(); - } + $type = $property->getType(); + $typeName = null; - $properties[$property->getName()] = [ - 'name' => $property->getName(), - 'type' => $typeName, - ]; + if ($type) { + $typeName = (string) $type; } - } catch (\ReflectionException $e) { - return []; + + $properties[$property->getName()] = [ + 'name' => $property->getName(), + 'type' => $typeName, + ]; } return $properties; @@ -229,7 +238,7 @@ protected function generateBodyValidation(Endpoint $endpoint): string { $resourceType = $this->getResourceTypeFromEndpoint($endpoint); $dtoClassName = $this->getDtoClassName($endpoint); - $properties = $this->getDtoPropertiesViaReflection($dtoClassName); + $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); if (empty($properties)) { return $this->generateFallbackBodyValidation($resourceType, $endpoint); @@ -246,14 +255,16 @@ protected function generateBodyValidation(Endpoint $endpoint): string $lines[] = ' // POST calls dont have an ID field'; } - $lines[] = " ->data->type->toBe('{$resourceType}')"; - // Generate attribute validations $attributeValidations = $this->generateAttributeValidationsFromDto($properties); if ($attributeValidations) { + $lines[] = " ->data->type->toBe('{$resourceType}')"; $lines[] = ' ->data->attributes->scoped(fn ($attributes) => $attributes'; $lines[] = $attributeValidations; $lines[] = ' );'; + } else { + // No attributes to validate, just close the chain + $lines[] = " ->data->type->toBe('{$resourceType}');"; } $lines[] = ''; diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index b5051e5..97ce767 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -6,6 +6,7 @@ use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; +use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; @@ -16,9 +17,12 @@ class SingularGetRequestTestGenerator protected ApiSpecification $specification; - public function __construct(ApiSpecification $specification) + protected GeneratedCode $generatedCode; + + public function __construct(ApiSpecification $specification, GeneratedCode $generatedCode) { $this->specification = $specification; + $this->generatedCode = $generatedCode; } /** diff --git a/generator/generate.php b/generator/generate.php index 09fa508..6ae785b 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -74,15 +74,15 @@ dtoNamespaceSuffix: 'Dto', ); -// STEP 1: Generate SDK code (WITHOUT tests) -echo "šŸ—ļø Generating SDK with JSON:API models...\n"; +// Generate SDK code with tests in a single run +echo "šŸ—ļø Generating SDK with JSON:API models and tests...\n"; $generator = new CodeGenerator( config: $config, connectorGenerator: new JsonApiConnectorGenerator($config), dtoGenerator: new JsonApiDtoGenerator($config), requestGenerator: new JsonApiRequestGenerator($config), resourceGenerator: new JsonApiResourceGenerator($config), - postProcessors: [] // No test generator in first pass + postProcessors: [new JsonApiPestTestGenerator] // Generate tests in same run ); $result = $generator->run($specification); @@ -135,28 +135,10 @@ function writeFile($file, $outputDir, $namespace) echo ' āœ“ '.basename($path)."\n"; } -// STEP 2: Dump autoload so DTOs are available for reflection -echo "\n"; -passthru('composer dump-autoload --quiet'); - -// STEP 3: Generate tests (NOW that DTOs exist on disk and can be reflected) -echo "\n🧪 Generating tests...\n"; -$testGenerator = new CodeGenerator( - config: $config, - connectorGenerator: new JsonApiConnectorGenerator($config), - dtoGenerator: new JsonApiDtoGenerator($config), - requestGenerator: new JsonApiRequestGenerator($config), - resourceGenerator: new JsonApiResourceGenerator($config), - postProcessors: [new JsonApiPestTestGenerator] -); - -$testResult = $testGenerator->run($specification); -$tests = $testResult->additionalFiles ?? null; - -// Write test files (also apply path parameter transformations here as fallback) -if ($tests && is_array($tests)) { - echo "🧪 Tests:\n"; - foreach ($tests as $file) { +// Write test files +if ($result->additionalFiles && is_array($result->additionalFiles)) { + echo "\n🧪 Tests:\n"; + foreach ($result->additionalFiles as $file) { if ($file instanceof \Crescat\SaloonSdkGenerator\Data\TaggedOutputFile) { $testPath = __DIR__.'/../'.$file->path; @@ -171,3 +153,7 @@ function writeFile($file, $outputDir, $namespace) } } } + +// Dump autoload to make new classes available +echo "\n"; +passthru('composer dump-autoload --quiet'); diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index 1d07c53..466bc6e 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -1,19 +1,15 @@ timaticConnector = new Timatic\SDK\TimaticConnector( - - ); + $this->timaticConnector = new Timatic\SDK\TimaticConnector; }); - it('calls the postCorrections method in the Correction resource', function () { $mockClient = Saloon::fake([ PostCorrectionsRequest::class => MockResponse::make([], 200), @@ -31,13 +27,12 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('correction') + ->data->type->toBe('correction'); return true; }); }); - it('calls the patchCorrection method in the Correction resource', function () { $mockClient = Saloon::fake([ PatchCorrectionRequest::class => MockResponse::make([], 200), @@ -54,7 +49,7 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('correction') + ->data->type->toBe('correction'); return true; }); From d92489490dba204db1ac206fa517d258cb7cc53d Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 17:27:29 +0100 Subject: [PATCH 38/62] add DTO support to requests --- generator/JsonApiRequestGenerator.php | 103 ++++++++++++++++-- .../Approve/PostOvertimeApproveRequest.php | 14 +++ src/Requests/Budget/GetBudgetRequest.php | 14 +++ src/Requests/Budget/GetBudgetsRequest.php | 14 +++ src/Requests/Budget/PatchBudgetRequest.php | 14 +++ src/Requests/Budget/PostBudgetsRequest.php | 14 +++ .../GetBudgetTimeSpentTotalsRequest.php | 14 +++ .../BudgetType/GetBudgetTypesRequest.php | 14 +++ src/Requests/Change/GetChangeRequest.php | 14 +++ src/Requests/Change/GetChangesRequest.php | 14 +++ .../Correction/PatchCorrectionRequest.php | 14 +++ .../Correction/PostCorrectionsRequest.php | 14 +++ src/Requests/Customer/GetCustomerRequest.php | 14 +++ src/Requests/Customer/GetCustomersRequest.php | 14 +++ .../Customer/PatchCustomerRequest.php | 14 +++ .../Customer/PostCustomersRequest.php | 14 +++ .../GetDailyProgressesRequest.php | 14 +++ .../GetBudgetEntriesExportRequest.php | 14 +++ src/Requests/Entry/GetEntriesRequest.php | 14 +++ src/Requests/Entry/GetEntryRequest.php | 14 +++ src/Requests/Entry/PatchEntryRequest.php | 14 +++ src/Requests/Entry/PostEntriesRequest.php | 14 +++ .../GetEntrySuggestionRequest.php | 14 +++ .../GetEntrySuggestionsRequest.php | 14 +++ src/Requests/Event/PostEventsRequest.php | 14 +++ .../GetBudgetsExportMailsRequest.php | 14 +++ src/Requests/Incident/GetIncidentRequest.php | 14 +++ src/Requests/Incident/GetIncidentsRequest.php | 14 +++ .../PostOvertimeMarkAsExportedRequest.php | 14 +++ .../PostEntryMarkAsInvoicedRequest.php | 14 +++ src/Requests/Me/GetMesRequest.php | 14 +++ .../Number/GetIncidentsNumberRequest.php | 14 +++ src/Requests/Overtime/GetOvertimesRequest.php | 14 +++ .../Period/GetBudgetPeriodsRequest.php | 14 +++ src/Requests/Team/GetTeamRequest.php | 14 +++ src/Requests/Team/GetTeamsRequest.php | 14 +++ src/Requests/Team/PatchTeamRequest.php | 14 +++ src/Requests/Team/PostTeamsRequest.php | 14 +++ .../GetTimeSpentTotalsRequest.php | 14 +++ src/Requests/User/GetUserRequest.php | 14 +++ src/Requests/User/GetUsersRequest.php | 14 +++ src/Requests/User/PatchUserRequest.php | 14 +++ src/Requests/User/PostUsersRequest.php | 14 +++ .../GetUserCustomerHoursAggregatesRequest.php | 14 +++ 44 files changed, 695 insertions(+), 10 deletions(-) diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 7229ae5..535995e 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -8,10 +8,13 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; use Crescat\SaloonSdkGenerator\Helpers\MethodGeneratorHelper; +use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Nette\PhpGenerator\ClassType; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Facades\Hydrator; class JsonApiRequestGenerator extends RequestGenerator { @@ -54,18 +57,21 @@ protected function getConstructorParameterName(string $originalName, bool $isPat */ protected function customizeRequestClass(ClassType $classType, $namespace, Endpoint $endpoint): void { - if (! $this->isCollectionRequest($endpoint)) { - return; + if ($this->isCollectionRequest($endpoint)) { + // Add Paginatable interface to all collection requests + $namespace->addUse(Paginatable::class); + $classType->addImplement(Paginatable::class); + + // Add HasFilters trait if collection has filter parameters in the endpoint + if ($this->hasFilterParameters($endpoint)) { + $namespace->addUse(HasFilters::class); + $classType->addTrait(HasFilters::class); + } } - // Add Paginatable interface to all collection requests - $namespace->addUse(Paginatable::class); - $classType->addImplement(Paginatable::class); - - // Add HasFilters trait if collection has filter parameters in the endpoint - if ($this->hasFilterParameters($endpoint)) { - $namespace->addUse(HasFilters::class); - $classType->addTrait(HasFilters::class); + // Add hydration support to GET, POST, and PATCH requests + if ($this->shouldHaveHydration($endpoint)) { + $this->addHydrationSupport($classType, $namespace, $endpoint); } } @@ -142,4 +148,81 @@ protected function hasFilterParameters(Endpoint $endpoint): bool return false; } + + /** + * Determine if request should have hydration support + */ + protected function shouldHaveHydration(Endpoint $endpoint): bool + { + // Add hydration to GET, POST, and PATCH requests + return $endpoint->method->isGet() + || $endpoint->method->isPost() + || $endpoint->method->isPatch(); + } + + /** + * Add hydration support to request class + */ + protected function addHydrationSupport(ClassType $classType, $namespace, Endpoint $endpoint): void + { + // Determine DTO class name from endpoint + $dtoClassName = $this->getDtoClassName($endpoint); + + // Add imports + $namespace->addUse(Hydrator::class); + $namespace->addUse(Response::class); + $namespace->addUse("Timatic\\SDK\\Dto\\{$dtoClassName}"); + + // Add $model property - use the imported class name with ::class + $classType->addProperty('model') + ->setProtected() + ->setValue(new \Nette\PhpGenerator\Literal("{$dtoClassName}::class")); + + // Add createDtoFromResponse method + $method = $classType->addMethod('createDtoFromResponse') + ->setReturnType('mixed'); + + $param = $method->addParameter('response'); + $param->setType(Response::class); + + // Use appropriate hydration method based on request type + if ($this->isCollectionRequest($endpoint)) { + // Collection: use hydrateCollection + $method->addBody('return Hydrator::hydrateCollection('); + $method->addBody(' $this->model,'); + $method->addBody(' $response->json(\'data\'),'); + $method->addBody(' $response->json(\'included\')'); + $method->addBody(');'); + } else { + // Single resource: use hydrate + $method->addBody('return Hydrator::hydrate('); + $method->addBody(' $this->model,'); + $method->addBody(' $response->json(\'data\'),'); + $method->addBody(' $response->json(\'included\')'); + $method->addBody(');'); + } + } + + /** + * Get DTO class name from endpoint + */ + protected function getDtoClassName(Endpoint $endpoint): string + { + // Use collection name to determine DTO + if ($endpoint->collection) { + $resourceName = NameHelper::resourceClassName($endpoint->collection); + + // Remove trailing 's' for singular DTO name + return rtrim($resourceName, 's'); + } + + // Fallback: try to parse from endpoint name + $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); + // Remove method prefix (post, patch, get) + $name = preg_replace('/^(post|patch|get)/i', '', $name); + // Remove trailing 's' for singular + $name = rtrim($name, 's'); + + return NameHelper::resourceClassName($name); + } } diff --git a/src/Requests/Approve/PostOvertimeApproveRequest.php b/src/Requests/Approve/PostOvertimeApproveRequest.php index 5777ac7..07d6b42 100644 --- a/src/Requests/Approve/PostOvertimeApproveRequest.php +++ b/src/Requests/Approve/PostOvertimeApproveRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Approve; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postOvertimeApprove @@ -15,8 +18,19 @@ class PostOvertimeApproveRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Approve::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/overtimes/{$this->overtimeId}/approve"; diff --git a/src/Requests/Budget/GetBudgetRequest.php b/src/Requests/Budget/GetBudgetRequest.php index 00da2a7..920e0a2 100644 --- a/src/Requests/Budget/GetBudgetRequest.php +++ b/src/Requests/Budget/GetBudgetRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Budget; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudget */ class GetBudgetRequest extends Request { + protected $model = Budget::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/budgets/{$this->budgetId}"; diff --git a/src/Requests/Budget/GetBudgetsRequest.php b/src/Requests/Budget/GetBudgetsRequest.php index ed269ab..e126f42 100644 --- a/src/Requests/Budget/GetBudgetsRequest.php +++ b/src/Requests/Budget/GetBudgetsRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\Budget; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgets @@ -14,8 +17,19 @@ class GetBudgetsRequest extends Request implements Paginatable { use HasFilters; + protected $model = Budget::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/budgets'; diff --git a/src/Requests/Budget/PatchBudgetRequest.php b/src/Requests/Budget/PatchBudgetRequest.php index cb688b5..e20f8dd 100644 --- a/src/Requests/Budget/PatchBudgetRequest.php +++ b/src/Requests/Budget/PatchBudgetRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Budget; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * patchBudget @@ -15,8 +18,19 @@ class PatchBudgetRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Budget::class; + protected Method $method = Method::PATCH; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/budgets/{$this->budgetId}"; diff --git a/src/Requests/Budget/PostBudgetsRequest.php b/src/Requests/Budget/PostBudgetsRequest.php index 9b9f5cd..b1b40eb 100644 --- a/src/Requests/Budget/PostBudgetsRequest.php +++ b/src/Requests/Budget/PostBudgetsRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Budget; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postBudgets @@ -15,8 +18,19 @@ class PostBudgetsRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Budget::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/budgets'; diff --git a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php index 47d5136..9c61cff 100644 --- a/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php +++ b/src/Requests/BudgetTimeSpentTotal/GetBudgetTimeSpentTotalsRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\BudgetTimeSpentTotal; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetTimeSpentTotals @@ -14,8 +17,19 @@ class GetBudgetTimeSpentTotalsRequest extends Request implements Paginatable { use HasFilters; + protected $model = BudgetTimeSpentTotal::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/budget-time-spent-totals'; diff --git a/src/Requests/BudgetType/GetBudgetTypesRequest.php b/src/Requests/BudgetType/GetBudgetTypesRequest.php index b3a8f24..0197a88 100644 --- a/src/Requests/BudgetType/GetBudgetTypesRequest.php +++ b/src/Requests/BudgetType/GetBudgetTypesRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\BudgetType; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetTypes */ class GetBudgetTypesRequest extends Request implements Paginatable { + protected $model = BudgetType::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/budget-types'; diff --git a/src/Requests/Change/GetChangeRequest.php b/src/Requests/Change/GetChangeRequest.php index 9cdb833..085a25e 100644 --- a/src/Requests/Change/GetChangeRequest.php +++ b/src/Requests/Change/GetChangeRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Change; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getChange */ class GetChangeRequest extends Request { + protected $model = Change::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/changes/{$this->changeId}"; diff --git a/src/Requests/Change/GetChangesRequest.php b/src/Requests/Change/GetChangesRequest.php index 9a26355..2192a97 100644 --- a/src/Requests/Change/GetChangesRequest.php +++ b/src/Requests/Change/GetChangesRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\Change; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getChanges */ class GetChangesRequest extends Request implements Paginatable { + protected $model = Change::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/changes'; diff --git a/src/Requests/Correction/PatchCorrectionRequest.php b/src/Requests/Correction/PatchCorrectionRequest.php index 69e2464..f5e535e 100644 --- a/src/Requests/Correction/PatchCorrectionRequest.php +++ b/src/Requests/Correction/PatchCorrectionRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Correction; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * patchCorrection @@ -15,8 +18,19 @@ class PatchCorrectionRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Correction::class; + protected Method $method = Method::PATCH; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/corrections/{$this->correctionId}"; diff --git a/src/Requests/Correction/PostCorrectionsRequest.php b/src/Requests/Correction/PostCorrectionsRequest.php index 3421db4..ef756f2 100644 --- a/src/Requests/Correction/PostCorrectionsRequest.php +++ b/src/Requests/Correction/PostCorrectionsRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Correction; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postCorrections @@ -15,8 +18,19 @@ class PostCorrectionsRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Correction::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/corrections'; diff --git a/src/Requests/Customer/GetCustomerRequest.php b/src/Requests/Customer/GetCustomerRequest.php index 36054ce..08f46a0 100644 --- a/src/Requests/Customer/GetCustomerRequest.php +++ b/src/Requests/Customer/GetCustomerRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Customer; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getCustomer */ class GetCustomerRequest extends Request { + protected $model = Customer::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/customers/{$this->customerId}"; diff --git a/src/Requests/Customer/GetCustomersRequest.php b/src/Requests/Customer/GetCustomersRequest.php index 50091bc..5ef2f5a 100644 --- a/src/Requests/Customer/GetCustomersRequest.php +++ b/src/Requests/Customer/GetCustomersRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\Customer; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getCustomers @@ -14,8 +17,19 @@ class GetCustomersRequest extends Request implements Paginatable { use HasFilters; + protected $model = Customer::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/customers'; diff --git a/src/Requests/Customer/PatchCustomerRequest.php b/src/Requests/Customer/PatchCustomerRequest.php index e0b1607..256f900 100644 --- a/src/Requests/Customer/PatchCustomerRequest.php +++ b/src/Requests/Customer/PatchCustomerRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Customer; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * patchCustomer @@ -15,8 +18,19 @@ class PatchCustomerRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Customer::class; + protected Method $method = Method::PATCH; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/customers/{$this->customerId}"; diff --git a/src/Requests/Customer/PostCustomersRequest.php b/src/Requests/Customer/PostCustomersRequest.php index 59075e1..eee484d 100644 --- a/src/Requests/Customer/PostCustomersRequest.php +++ b/src/Requests/Customer/PostCustomersRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Customer; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postCustomers @@ -15,8 +18,19 @@ class PostCustomersRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Customer::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/customers'; diff --git a/src/Requests/DailyProgress/GetDailyProgressesRequest.php b/src/Requests/DailyProgress/GetDailyProgressesRequest.php index 3a4461a..25c7416 100644 --- a/src/Requests/DailyProgress/GetDailyProgressesRequest.php +++ b/src/Requests/DailyProgress/GetDailyProgressesRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\DailyProgre; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getDailyProgresses */ class GetDailyProgressesRequest extends Request implements Paginatable { + protected $model = DailyProgre::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/daily-progress'; diff --git a/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php index 1cb5221..24b5a28 100644 --- a/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php +++ b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\EntriesExport; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetEntriesExport */ class GetBudgetEntriesExportRequest extends Request { + protected $model = EntriesExport::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/budgets/{$this->budgetId}/entries-export"; diff --git a/src/Requests/Entry/GetEntriesRequest.php b/src/Requests/Entry/GetEntriesRequest.php index d654b7b..8c4b3f6 100644 --- a/src/Requests/Entry/GetEntriesRequest.php +++ b/src/Requests/Entry/GetEntriesRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\Entry; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getEntries @@ -14,8 +17,19 @@ class GetEntriesRequest extends Request implements Paginatable { use HasFilters; + protected $model = Entry::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/entries'; diff --git a/src/Requests/Entry/GetEntryRequest.php b/src/Requests/Entry/GetEntryRequest.php index 7d6924a..a6919b4 100644 --- a/src/Requests/Entry/GetEntryRequest.php +++ b/src/Requests/Entry/GetEntryRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Entry; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getEntry */ class GetEntryRequest extends Request { + protected $model = Entry::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/entries/{$this->entryId}"; diff --git a/src/Requests/Entry/PatchEntryRequest.php b/src/Requests/Entry/PatchEntryRequest.php index a827432..a6fe5e1 100644 --- a/src/Requests/Entry/PatchEntryRequest.php +++ b/src/Requests/Entry/PatchEntryRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Entry; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * patchEntry @@ -15,8 +18,19 @@ class PatchEntryRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Entry::class; + protected Method $method = Method::PATCH; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/entries/{$this->entryId}"; diff --git a/src/Requests/Entry/PostEntriesRequest.php b/src/Requests/Entry/PostEntriesRequest.php index 8a2809e..78c34bf 100644 --- a/src/Requests/Entry/PostEntriesRequest.php +++ b/src/Requests/Entry/PostEntriesRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Entry; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postEntries @@ -15,8 +18,19 @@ class PostEntriesRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Entry::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/entries'; diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php b/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php index d48fcc2..4494213 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestionRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\EntrySuggestion; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getEntrySuggestion */ class GetEntrySuggestionRequest extends Request { + protected $model = EntrySuggestion::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/entry-suggestions/{$this->entrySuggestionId}"; diff --git a/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php b/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php index 2528e59..5e1239a 100644 --- a/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php +++ b/src/Requests/EntrySuggestion/GetEntrySuggestionsRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\EntrySuggestion; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getEntrySuggestions @@ -14,8 +17,19 @@ class GetEntrySuggestionsRequest extends Request implements Paginatable { use HasFilters; + protected $model = EntrySuggestion::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/entry-suggestions'; diff --git a/src/Requests/Event/PostEventsRequest.php b/src/Requests/Event/PostEventsRequest.php index 266ac56..32c33b1 100644 --- a/src/Requests/Event/PostEventsRequest.php +++ b/src/Requests/Event/PostEventsRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Event; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postEvents @@ -15,8 +18,19 @@ class PostEventsRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Event::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/events'; diff --git a/src/Requests/ExportMail/GetBudgetsExportMailsRequest.php b/src/Requests/ExportMail/GetBudgetsExportMailsRequest.php index 80f2676..6aa927b 100644 --- a/src/Requests/ExportMail/GetBudgetsExportMailsRequest.php +++ b/src/Requests/ExportMail/GetBudgetsExportMailsRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\ExportMail; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetsExportMails */ class GetBudgetsExportMailsRequest extends Request implements Paginatable { + protected $model = ExportMail::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/budgets/export-mail'; diff --git a/src/Requests/Incident/GetIncidentRequest.php b/src/Requests/Incident/GetIncidentRequest.php index 41c0265..82b1cfa 100644 --- a/src/Requests/Incident/GetIncidentRequest.php +++ b/src/Requests/Incident/GetIncidentRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Incident; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getIncident */ class GetIncidentRequest extends Request { + protected $model = Incident::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/incidents/{$this->incidentId}"; diff --git a/src/Requests/Incident/GetIncidentsRequest.php b/src/Requests/Incident/GetIncidentsRequest.php index 77770fa..b1d73a8 100644 --- a/src/Requests/Incident/GetIncidentsRequest.php +++ b/src/Requests/Incident/GetIncidentsRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\Incident; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getIncidents */ class GetIncidentsRequest extends Request implements Paginatable { + protected $model = Incident::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/incidents'; diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php index 531b0bc..50c9d07 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\MarkAsExported; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postOvertimeMarkAsExported @@ -15,8 +18,19 @@ class PostOvertimeMarkAsExportedRequest extends Request implements HasBody { use HasJsonBody; + protected $model = MarkAsExported::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/overtimes/{$this->overtimeId}/mark-as-exported"; diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php index 7766570..a5949ef 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\MarkAsInvoiced; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postEntryMarkAsInvoiced @@ -15,8 +18,19 @@ class PostEntryMarkAsInvoicedRequest extends Request implements HasBody { use HasJsonBody; + protected $model = MarkAsInvoiced::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/entries/{$this->entryId}/mark-as-invoiced"; diff --git a/src/Requests/Me/GetMesRequest.php b/src/Requests/Me/GetMesRequest.php index 107fd45..973d686 100644 --- a/src/Requests/Me/GetMesRequest.php +++ b/src/Requests/Me/GetMesRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\Me; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getMes */ class GetMesRequest extends Request implements Paginatable { + protected $model = Me::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/me'; diff --git a/src/Requests/Number/GetIncidentsNumberRequest.php b/src/Requests/Number/GetIncidentsNumberRequest.php index 911051b..c3d6c23 100644 --- a/src/Requests/Number/GetIncidentsNumberRequest.php +++ b/src/Requests/Number/GetIncidentsNumberRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Number; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getIncidentsNumber */ class GetIncidentsNumberRequest extends Request { + protected $model = Number::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/incidents/number/{$this->incidentId}"; diff --git a/src/Requests/Overtime/GetOvertimesRequest.php b/src/Requests/Overtime/GetOvertimesRequest.php index fcbcb93..18541ad 100644 --- a/src/Requests/Overtime/GetOvertimesRequest.php +++ b/src/Requests/Overtime/GetOvertimesRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\Overtime; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getOvertimes @@ -14,8 +17,19 @@ class GetOvertimesRequest extends Request implements Paginatable { use HasFilters; + protected $model = Overtime::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/overtimes'; diff --git a/src/Requests/Period/GetBudgetPeriodsRequest.php b/src/Requests/Period/GetBudgetPeriodsRequest.php index ddfa825..af6beed 100644 --- a/src/Requests/Period/GetBudgetPeriodsRequest.php +++ b/src/Requests/Period/GetBudgetPeriodsRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Period; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetPeriods */ class GetBudgetPeriodsRequest extends Request { + protected $model = Period::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/budgets/{$this->budgetId}/periods"; diff --git a/src/Requests/Team/GetTeamRequest.php b/src/Requests/Team/GetTeamRequest.php index 9ab0902..291d57d 100644 --- a/src/Requests/Team/GetTeamRequest.php +++ b/src/Requests/Team/GetTeamRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\Team; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getTeam */ class GetTeamRequest extends Request { + protected $model = Team::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/teams/{$this->teamId}"; diff --git a/src/Requests/Team/GetTeamsRequest.php b/src/Requests/Team/GetTeamsRequest.php index eeda391..696b165 100644 --- a/src/Requests/Team/GetTeamsRequest.php +++ b/src/Requests/Team/GetTeamsRequest.php @@ -4,15 +4,29 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; +use Timatic\SDK\Dto\Team; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getTeams */ class GetTeamsRequest extends Request implements Paginatable { + protected $model = Team::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/teams'; diff --git a/src/Requests/Team/PatchTeamRequest.php b/src/Requests/Team/PatchTeamRequest.php index 12a9c36..fab990e 100644 --- a/src/Requests/Team/PatchTeamRequest.php +++ b/src/Requests/Team/PatchTeamRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Team; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * patchTeam @@ -15,8 +18,19 @@ class PatchTeamRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Team::class; + protected Method $method = Method::PATCH; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/teams/{$this->teamId}"; diff --git a/src/Requests/Team/PostTeamsRequest.php b/src/Requests/Team/PostTeamsRequest.php index 2ea80a4..5e2651c 100644 --- a/src/Requests/Team/PostTeamsRequest.php +++ b/src/Requests/Team/PostTeamsRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\Team; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postTeams @@ -15,8 +18,19 @@ class PostTeamsRequest extends Request implements HasBody { use HasJsonBody; + protected $model = Team::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/teams'; diff --git a/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php b/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php index 892dc54..4e8624c 100644 --- a/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php +++ b/src/Requests/TimeSpentTotal/GetTimeSpentTotalsRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\TimeSpentTotal; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getTimeSpentTotals @@ -14,8 +17,19 @@ class GetTimeSpentTotalsRequest extends Request implements Paginatable { use HasFilters; + protected $model = TimeSpentTotal::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/time-spent-totals'; diff --git a/src/Requests/User/GetUserRequest.php b/src/Requests/User/GetUserRequest.php index 34b11a4..b91dc81 100644 --- a/src/Requests/User/GetUserRequest.php +++ b/src/Requests/User/GetUserRequest.php @@ -4,14 +4,28 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +use Timatic\SDK\Dto\User; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getUser */ class GetUserRequest extends Request { + protected $model = User::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/users/{$this->userId}"; diff --git a/src/Requests/User/GetUsersRequest.php b/src/Requests/User/GetUsersRequest.php index 1cbc392..33c1572 100644 --- a/src/Requests/User/GetUsersRequest.php +++ b/src/Requests/User/GetUsersRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\User; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getUsers @@ -14,8 +17,19 @@ class GetUsersRequest extends Request implements Paginatable { use HasFilters; + protected $model = User::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/users'; diff --git a/src/Requests/User/PatchUserRequest.php b/src/Requests/User/PatchUserRequest.php index 84b69ca..a8124e3 100644 --- a/src/Requests/User/PatchUserRequest.php +++ b/src/Requests/User/PatchUserRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\User; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * patchUser @@ -15,8 +18,19 @@ class PatchUserRequest extends Request implements HasBody { use HasJsonBody; + protected $model = User::class; + protected Method $method = Method::PATCH; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return "/users/{$this->userId}"; diff --git a/src/Requests/User/PostUsersRequest.php b/src/Requests/User/PostUsersRequest.php index 34110a1..bdc246e 100644 --- a/src/Requests/User/PostUsersRequest.php +++ b/src/Requests/User/PostUsersRequest.php @@ -5,8 +5,11 @@ use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Dto\User; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * postUsers @@ -15,8 +18,19 @@ class PostUsersRequest extends Request implements HasBody { use HasJsonBody; + protected $model = User::class; + protected Method $method = Method::POST; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrate( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/users'; diff --git a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php index 9412e7d..61c1239 100644 --- a/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php +++ b/src/Requests/UserCustomerHoursAggregate/GetUserCustomerHoursAggregatesRequest.php @@ -4,8 +4,11 @@ use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; +use Timatic\SDK\Dto\UserCustomerHoursAggregate; +use Timatic\SDK\Hydration\Facades\Hydrator; /** * getUserCustomerHoursAggregates @@ -14,8 +17,19 @@ class GetUserCustomerHoursAggregatesRequest extends Request implements Paginatab { use HasFilters; + protected $model = UserCustomerHoursAggregate::class; + protected Method $method = Method::GET; + public function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } + public function resolveEndpoint(): string { return '/user-customer-hours-aggregates'; From 1a13bedf5e0d54c961d226f7250ff7941158792d Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 18:20:48 +0100 Subject: [PATCH 39/62] add DTO hydration tests --- CLAUDE.md | 2 +- claude-todo.md | 36 +++- generator/JsonApiRequestGenerator.php | 10 +- .../CollectionRequestTestGenerator.php | 85 +++++--- .../MutationRequestTestGenerator.php | 18 +- .../SingularGetRequestTestGenerator.php | 51 ++++- .../TestGenerators/Traits/DtoAssertions.php | 200 ++++++++++++++++++ .../Traits/SchemaExtractorTrait.php | 23 +- .../pest-collection-request-test-func.stub | 5 + .../stubs/pest-resource-test.stub | 1 + .../pest-singular-get-request-test-func.stub | 5 + .../GetDailyProgressesRequest.php | 4 +- tests/Requests/ApproveTest.php | 4 +- tests/Requests/BudgetTest.php | 88 +++++++- tests/Requests/BudgetTimeSpentTotalTest.php | 22 +- tests/Requests/BudgetTypeTest.php | 33 ++- tests/Requests/ChangeTest.php | 10 + tests/Requests/CorrectionTest.php | 6 +- tests/Requests/CustomerTest.php | 35 ++- tests/Requests/DailyProgressTest.php | 16 +- tests/Requests/EntriesExportTest.php | 5 + tests/Requests/EntrySuggestionTest.php | 51 ++++- tests/Requests/EntryTest.php | 142 ++++++++++++- tests/Requests/EventTest.php | 2 +- tests/Requests/ExportMailTest.php | 5 + tests/Requests/IncidentTest.php | 10 + tests/Requests/MarkAsExportedTest.php | 4 +- tests/Requests/MarkAsInvoicedTest.php | 4 +- tests/Requests/MeTest.php | 5 + tests/Requests/NumberTest.php | 5 + tests/Requests/OvertimeTest.php | 30 ++- tests/Requests/PeriodTest.php | 5 + tests/Requests/TeamTest.php | 25 ++- tests/Requests/TimeSpentTotalTest.php | 25 ++- .../UserCustomerHoursAggregateTest.php | 21 +- tests/Requests/UserTest.php | 27 ++- 36 files changed, 913 insertions(+), 107 deletions(-) create mode 100644 generator/TestGenerators/Traits/DtoAssertions.php diff --git a/CLAUDE.md b/CLAUDE.md index 4233068..addbfc6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,7 +11,7 @@ The Laravel Boost guidelines are specifically curated by Laravel maintainers for This application is a Laravel application and its main Laravel ecosystems package & versions are below. You are an expert with them all. Ensure you abide by these specific packages & versions. - php - 8.4.14 -- laravel/framework (LARAVEL) - v11 +- laravel/framework (LARAVEL) - v12 - laravel/horizon (HORIZON) - v5 - laravel/pint (PINT) - v1 - laravel/prompts (PROMPTS) - v0 diff --git a/claude-todo.md b/claude-todo.md index d36c496..4e4cb37 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -1,21 +1,45 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. +## Task: apply Hydration for Requests + +- Add `protected $model = Budget::class;` to the BudgetClass for example +- All GET, POST, PATCH request need to implement createDtoFromResponse +``` +function createDtoFromResponse(Response $response): mixed + { + return Hydrator::hydrateCollection( + $this->model, + $response->json('data'), + $response->json('included') + ); + } +``` + ## Task 3: Validate DTO Hydration in GET Tests **Goal:** Verify JSON:API responses properly populate DTOs +Test the fields that are set in the json to be present on the DTO. +``` + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->budgetTypeId->toBe("budget-type-xyz") + ->customerId->toBe("customer-abc") + ->showToCustomer->toBe(true) + ->startedAt->toEqual(new Carbon("2025-11-22T10:40:04.065Z")) +``` + **Changes:** - Add expectations after response: `expect($data['type'])->toBe('entries')` -- Validate `data.id`, `data.attributes` structure -- Check 2-3 key DTO properties are present with expected values +- Check DTO properties are present with expected values - Regenerate with `composer regenerate` and test with `composer test` -## TASK: replace "type: resource" with actual resource in mocked GET data +## TASK: fix the test that have -## TASK 5: Add params for filters in Resources +## TASK: Remove Me Request, Me Resource and Me Test -- Make a plan how we can add filters like $timaticConnector->budget->getBudgets(['key'=>'value']); -- or come up with a better way to add filters to resources +## TASK: replace "type: resource" with actual resource in mocked GET data ## Gebruik raw JSON alleen om operationId → schema reference mapping te vinden (totdat we vendor package kunnen patchen) diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 535995e..77f11d1 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -9,6 +9,7 @@ use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; use Crescat\SaloonSdkGenerator\Helpers\MethodGeneratorHelper; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Illuminate\Support\Str; use Nette\PhpGenerator\ClassType; use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; @@ -212,17 +213,16 @@ protected function getDtoClassName(Endpoint $endpoint): string if ($endpoint->collection) { $resourceName = NameHelper::resourceClassName($endpoint->collection); - // Remove trailing 's' for singular DTO name - return rtrim($resourceName, 's'); + // Use Laravel's Str::singular() for correct singular form + return Str::singular($resourceName); } // Fallback: try to parse from endpoint name $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); // Remove method prefix (post, patch, get) $name = preg_replace('/^(post|patch|get)/i', '', $name); - // Remove trailing 's' for singular - $name = rtrim($name, 's'); - return NameHelper::resourceClassName($name); + // Use Laravel's Str::singular() for correct singular form + return Str::singular(NameHelper::resourceClassName($name)); } } diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 88b387b..73c45ba 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -8,12 +8,15 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Illuminate\Support\Str; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; class CollectionRequestTestGenerator { + use DtoAssertions; use MockDataGeneratorTrait; use SchemaExtractorTrait; use TestValueGeneratorTrait; @@ -73,6 +76,19 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): $functionStub ); + // Generate DTO assertions based on mock data + $mockData = $this->generateMockData($endpoint); + $dtoAssertions = $this->generateDtoAssertions($mockData); + + // If no valid assertions (comments only), remove the DTO validation block entirely + if (str_starts_with(trim($dtoAssertions), '//')) { + // Remove the entire DTO validation block + $pattern = '/(.*\$response->status\(\)\)->toBe\(200\);.*?)(\n\s*\$dtoCollection = \$response->dto\(\);.*?{{ dtoAssertions }};)/s'; + $functionStub = preg_replace($pattern, '$1', $functionStub); + } else { + $functionStub = str_replace('{{ dtoAssertions }}', $dtoAssertions, $functionStub); + } + return $functionStub; } @@ -81,43 +97,64 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): */ protected function generateMockData(Endpoint $endpoint): array { + // Get DTO class name from endpoint + $dtoClassName = $this->getDtoClassName($endpoint); + // Try to determine the schema for this endpoint $schema = $this->getResponseSchemaForEndpoint($endpoint); - if ($schema) { - // Generate mock data based on schema + if (! $schema) { + throw new \Exception('Endpoint does not exist'); + } + + // Generate mock data based on DTO if available, otherwise fallback to schema + $attributes = $this->generateMockAttributesFromDto($dtoClassName); + if (empty($attributes) || $attributes === ['name' => 'Mock value']) { + // Fallback to schema-based generation $attributes = $this->generateMockAttributes($schema); - $resourceType = $this->getResourceTypeFromSchema($schema); - - // Generate 2-3 items for collections - return [ - 'data' => [ - [ - 'type' => $resourceType, - 'id' => 'mock-id-1', - 'attributes' => $attributes, - ], - [ - 'type' => $resourceType, - 'id' => 'mock-id-2', - 'attributes' => $this->generateMockAttributes($schema), - ], - ], - ]; } - // Fallback: generic mock data - $resourceName = NameHelper::resourceClassName($endpoint->collection); - $resourceType = NameHelper::safeVariableName($resourceName); + $resourceType = $this->getResourceTypeFromSchema($schema); + // Generate 2-3 items for collections return [ 'data' => [ - ['type' => $resourceType, 'id' => 'mock-id-1', 'attributes' => ['name' => 'Mock item 1']], - ['type' => $resourceType, 'id' => 'mock-id-2', 'attributes' => ['name' => 'Mock item 2']], + [ + 'type' => $resourceType, + 'id' => 'mock-id-1', + 'attributes' => $attributes, + ], + [ + 'type' => $resourceType, + 'id' => 'mock-id-2', + 'attributes' => $attributes, + ], ], ]; } + /** + * Get DTO class name from endpoint + */ + protected function getDtoClassName(Endpoint $endpoint): string + { + // Use collection name to determine DTO + if ($endpoint->collection) { + $resourceName = NameHelper::resourceClassName($endpoint->collection); + + // Use Laravel's Str::singular() for correct singular form + return Str::singular($resourceName); + } + + // Fallback: try to parse from endpoint name + $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); + // Remove method prefix (get, post, patch) + $name = preg_replace('/^(get|post|patch)/i', '', $name); + + // Use Laravel's Str::singular() for correct singular form + return Str::singular(NameHelper::resourceClassName($name)); + } + /** * Generate the complete filter assertion block */ diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 4212c78..1517d28 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -9,6 +9,7 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; @@ -80,8 +81,12 @@ protected function generateMethodArguments(Endpoint $endpoint): string $args[] = "{$paramName}: 'test string'"; } - // Add $dto parameter last - $args[] = '$dto'; + // Add $dto parameter last - use named argument if there are path params + if (empty($endpoint->pathParameters)) { + $args[] = '$dto'; + } else { + $args[] = 'data: $dto'; + } return implode(', ', $args); } @@ -315,18 +320,17 @@ protected function getDtoClassName(Endpoint $endpoint): string if ($endpoint->collection) { $resourceName = NameHelper::resourceClassName($endpoint->collection); - // Remove trailing 's' for singular DTO name - return rtrim($resourceName, 's'); + // Use Laravel's Str::singular() for correct singular form + return Str::singular($resourceName); } // Fallback: try to parse from endpoint name $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); // Remove method prefix (post, patch) $name = preg_replace('/^(post|patch)/i', '', $name); - // Remove trailing 's' for singular - $name = rtrim($name, 's'); - return NameHelper::resourceClassName($name); + // Use Laravel's Str::singular() for correct singular form + return Str::singular(NameHelper::resourceClassName($name)); } /** diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index 97ce767..d74bc9d 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -7,11 +7,15 @@ use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; +use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Illuminate\Support\Str; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; class SingularGetRequestTestGenerator { + use DtoAssertions; use MockDataGeneratorTrait; use SchemaExtractorTrait; @@ -55,6 +59,19 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): $functionStub ); + // Generate DTO assertions based on mock data + $mockData = $this->generateMockData($endpoint); + $dtoAssertions = $this->generateDtoAssertions($mockData); + + // If no valid assertions (comments only), remove the DTO validation block entirely + if (str_starts_with(trim($dtoAssertions), '//')) { + // Remove the entire DTO validation block + $pattern = '/(.*\$response->status\(\)\)->toBe\(200\);.*?)(\n\s*\$dto = \$response->dto\(\);.*?{{ dtoAssertions }};)/s'; + $functionStub = preg_replace($pattern, '$1', $functionStub); + } else { + $functionStub = str_replace('{{ dtoAssertions }}', $dtoAssertions, $functionStub); + } + return $functionStub; } @@ -63,6 +80,9 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): */ protected function generateMockData(Endpoint $endpoint): array { + // Get DTO class name from endpoint + $dtoClassName = $this->getDtoClassName($endpoint); + // Try to determine the schema for this endpoint $schema = $this->getResponseSchemaForEndpoint($endpoint); @@ -70,8 +90,13 @@ protected function generateMockData(Endpoint $endpoint): array throw new \Exception('schema operation not found'); } - // Generate mock data based on schema - $attributes = $this->generateMockAttributes($schema); + // Generate mock data based on DTO if available, otherwise fallback to schema + $attributes = $this->generateMockAttributesFromDto($dtoClassName); + if (empty($attributes) || $attributes === ['name' => 'Mock value']) { + // Fallback to schema-based generation + $attributes = $this->generateMockAttributes($schema); + } + $resourceType = $this->getResourceTypeFromSchema($schema); return [ @@ -82,4 +107,26 @@ protected function generateMockData(Endpoint $endpoint): array ], ]; } + + /** + * Get DTO class name from endpoint + */ + protected function getDtoClassName(Endpoint $endpoint): string + { + // Use collection name to determine DTO + if ($endpoint->collection) { + $resourceName = NameHelper::resourceClassName($endpoint->collection); + + // Use Laravel's Str::singular() for correct singular form + return Str::singular($resourceName); + } + + // Fallback: try to parse from endpoint name + $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); + // Remove method prefix (get, post, patch) + $name = preg_replace('/^(get|post|patch)/i', '', $name); + + // Use Laravel's Str::singular() for correct singular form + return Str::singular(NameHelper::resourceClassName($name)); + } } diff --git a/generator/TestGenerators/Traits/DtoAssertions.php b/generator/TestGenerators/Traits/DtoAssertions.php new file mode 100644 index 0000000..1032181 --- /dev/null +++ b/generator/TestGenerators/Traits/DtoAssertions.php @@ -0,0 +1,200 @@ + $value) { + // Skip arrays completely + if (is_array($value)) { + continue; + } + + $assertion = $this->generateAssertionForValue($key, $value); + $assertions[] = $assertion; + } + + // If no valid assertions after filtering, return comment + if (empty($assertions)) { + return ' // No simple attributes to validate (arrays skipped)'; + } + + return implode("\n", $assertions); + } + + /** + * Get DTO properties from generated code + * + * @return array + */ + protected function getDtoPropertiesFromGeneratedCode(string $dtoClassName): array + { + // Check if DTO exists in generated code + if (! isset($this->generatedCode->dtoClasses[$dtoClassName])) { + return []; + } + + $phpFile = $this->generatedCode->dtoClasses[$dtoClassName]; + $properties = []; + + // Get the first namespace in the file + $namespace = array_values($phpFile->getNamespaces())[0] ?? null; + if (! $namespace) { + return []; + } + + // Get the first class in the namespace + $classType = array_values($namespace->getClasses())[0] ?? null; + if (! $classType) { + return []; + } + + // Extract properties from the class + foreach ($classType->getProperties() as $property) { + // Skip static properties + if ($property->isStatic()) { + continue; + } + + $type = $property->getType(); + $typeName = null; + + if ($type) { + $typeName = (string) $type; + } + + $properties[$property->getName()] = [ + 'name' => $property->getName(), + 'type' => $typeName, + ]; + } + + return $properties; + } + + /** + * Generate mock attributes from DTO properties + */ + protected function generateMockAttributesFromDto(string $dtoClassName): array + { + $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); + + if (empty($properties)) { + return ['name' => 'Mock value']; + } + + $attributes = []; + + foreach ($properties as $propInfo) { + $propName = $propInfo['name']; + + // Skip ID and timestamps - these are typically read-only + if (in_array($propName, ['id', 'createdAt', 'updatedAt', 'deletedAt'])) { + continue; + } + + $attributes[$propName] = $this->generateMockValueForDtoProperty($propName, $propInfo['type']); + } + + return $attributes; + } + + /** + * Generate a mock value for a DTO property based on its type + */ + protected function generateMockValueForDtoProperty(string $propertyName, ?string $typeName): mixed + { + if (! $typeName) { + return 'Mock value'; + } + + // Normalize type name (remove nullable prefix) + $typeName = ltrim($typeName, '?'); + + // DateTime fields + if (str_contains($typeName, 'Carbon') || str_contains($typeName, 'DateTime')) { + return '2025-11-22T10:40:04.065Z'; + } + + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return 'mock-id-123'; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return 'test@example.com'; + } + + // Type-based generation + return match ($typeName) { + 'bool' => true, + 'int' => 42, + 'float' => 3.14, + 'string' => 'Mock value', + 'array' => [], + default => 'Mock value', + }; + } + + /** + * Generate an assertion for a specific attribute value + */ + protected function generateAssertionForValue(string $key, mixed $value): string + { + // Handle different value types + if (is_bool($value)) { + $expected = $value ? 'true' : 'false'; + + return " ->{$key}->toBe({$expected})"; + } + + if (is_int($value)) { + return " ->{$key}->toBe({$value})"; + } + + if (is_null($value)) { + return " ->{$key}->toBeNull()"; + } + + // Check if it's a datetime string + if (is_string($value) && $this->isDateTimeString($value)) { + return " ->{$key}->toEqual(new Carbon(\"{$value}\"))"; + } + + // Default: string value + $escapedValue = addslashes($value); + + return " ->{$key}->toBe(\"{$escapedValue}\")"; + } + + /** + * Check if a string is a datetime format + */ + protected function isDateTimeString(string $value): bool + { + // Check for ISO 8601 format (e.g., 2025-11-22T10:40:04.065Z) + return (bool) preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/', $value); + } +} diff --git a/generator/TestGenerators/Traits/SchemaExtractorTrait.php b/generator/TestGenerators/Traits/SchemaExtractorTrait.php index 9c12a2b..7a2c911 100644 --- a/generator/TestGenerators/Traits/SchemaExtractorTrait.php +++ b/generator/TestGenerators/Traits/SchemaExtractorTrait.php @@ -10,6 +10,7 @@ use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Illuminate\Support\Str; trait SchemaExtractorTrait { @@ -210,19 +211,27 @@ protected function getResourceTypeFromSchema(Schema $schema): string } /** - * Get the resource type for JSON:API from endpoint (plural, lowercase) + * Get the resource type for JSON:API from endpoint (camelCase plural) + * API uses ->camel()->plural()->toString() for type determination */ protected function getResourceTypeFromEndpoint(Endpoint $endpoint): string { + $name = null; + if ($endpoint->collection) { - return NameHelper::safeVariableName($endpoint->collection); + $name = $endpoint->collection; + } else { + // Fallback: parse from endpoint path + $path = $endpoint->path; + // Extract first path segment (e.g., /budgets -> budgets) + preg_match('#^/([^/]+)#', $path, $matches); + $name = $matches[1] ?? 'resources'; } - // Fallback: parse from endpoint path - $path = $endpoint->path; - // Extract first path segment (e.g., /budgets -> budgets) - preg_match('#^/([^/]+)#', $path, $matches); + // Convert to camelCase and ensure plural using Laravel's Str helper + $camelName = NameHelper::safeVariableName($name); - return $matches[1] ?? 'resources'; + // Use Laravel's Str::plural() for correct English pluralization + return Str::plural($camelName); } } diff --git a/generator/TestGenerators/stubs/pest-collection-request-test-func.stub b/generator/TestGenerators/stubs/pest-collection-request-test-func.stub index f0b2801..5f4518c 100644 --- a/generator/TestGenerators/stubs/pest-collection-request-test-func.stub +++ b/generator/TestGenerators/stubs/pest-collection-request-test-func.stub @@ -12,4 +12,9 @@ it('{{ testDescription }}', function () { {{ filterAssertionBlock }} expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) +{{ dtoAssertions }}; }); diff --git a/generator/TestGenerators/stubs/pest-resource-test.stub b/generator/TestGenerators/stubs/pest-resource-test.stub index 9959b17..0aecca3 100644 --- a/generator/TestGenerators/stubs/pest-resource-test.stub +++ b/generator/TestGenerators/stubs/pest-resource-test.stub @@ -2,6 +2,7 @@ {{ dtoImports }} {{ requestImports }} +use Carbon\Carbon; use Saloon\Http\Faking\MockResponse; use Saloon\Http\Request; use Saloon\Laravel\Facades\Saloon; diff --git a/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub b/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub index cd5f232..ec99f1c 100644 --- a/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub +++ b/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub @@ -8,4 +8,9 @@ it('{{ testDescription }}', function () { Saloon::assertSent({{ requestClass }}::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) +{{ dtoAssertions }}; }); diff --git a/src/Requests/DailyProgress/GetDailyProgressesRequest.php b/src/Requests/DailyProgress/GetDailyProgressesRequest.php index 25c7416..224aa2f 100644 --- a/src/Requests/DailyProgress/GetDailyProgressesRequest.php +++ b/src/Requests/DailyProgress/GetDailyProgressesRequest.php @@ -6,7 +6,7 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; -use Timatic\SDK\Dto\DailyProgre; +use Timatic\SDK\Dto\DailyProgress; use Timatic\SDK\Hydration\Facades\Hydrator; /** @@ -14,7 +14,7 @@ */ class GetDailyProgressesRequest extends Request implements Paginatable { - protected $model = DailyProgre::class; + protected $model = DailyProgress::class; protected Method $method = Method::GET; diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index 016a2d3..feb3abe 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -22,14 +22,14 @@ $dto->endedAt = 'test value'; // todo: add every other DTO field - $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', $dto); + $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', data: $dto); Saloon::assertSent(PostOvertimeApproveRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('approve') + ->data->type->toBe('approves') ->data->attributes->scoped(fn ($attributes) => $attributes ->entryId->toBe('mock-id-123') ->overtimeTypeId->toBe('mock-id-123') diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 2e92fa3..4d8b136 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -1,5 +1,6 @@ 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'budgetTypeId' => 'mock-id-123', + 'customerId' => 'mock-id-123', + 'showToCustomer' => true, + 'changeId' => 'mock-id-123', + 'contractId' => 'mock-id-123', + 'title' => 'Mock value', + 'description' => 'Mock value', + 'totalPrice' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', + 'initialMinutes' => 42, + 'isArchived' => true, + 'renewalFrequency' => 'Mock value', + 'supervisorUserId' => 'mock-id-123', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'budgetTypeId' => 'mock-id-123', + 'customerId' => 'mock-id-123', + 'showToCustomer' => true, + 'changeId' => 'mock-id-123', + 'contractId' => 'mock-id-123', + 'title' => 'Mock value', + 'description' => 'Mock value', + 'totalPrice' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', + 'initialMinutes' => 42, + 'isArchived' => true, + 'renewalFrequency' => 'Mock value', + 'supervisorUserId' => 'mock-id-123', ], ], ], @@ -56,6 +83,24 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->budgetTypeId->toBe('mock-id-123') + ->customerId->toBe('mock-id-123') + ->showToCustomer->toBe(true) + ->changeId->toBe('mock-id-123') + ->contractId->toBe('mock-id-123') + ->title->toBe('Mock value') + ->description->toBe('Mock value') + ->totalPrice->toBe('Mock value') + ->startedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->endedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->initialMinutes->toBe(42) + ->isArchived->toBe(true) + ->renewalFrequency->toBe('Mock value') + ->supervisorUserId->toBe('mock-id-123'); }); it('calls the postBudgets method in the Budget resource', function () { @@ -78,7 +123,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('budget') + ->data->type->toBe('budgets') ->data->attributes->scoped(fn ($attributes) => $attributes ->budgetTypeId->toBe('mock-id-123') ->customerId->toBe('mock-id-123') @@ -97,7 +142,20 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'name' => 'Mock value', + 'budgetTypeId' => 'mock-id-123', + 'customerId' => 'mock-id-123', + 'showToCustomer' => true, + 'changeId' => 'mock-id-123', + 'contractId' => 'mock-id-123', + 'title' => 'Mock value', + 'description' => 'Mock value', + 'totalPrice' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', + 'initialMinutes' => 42, + 'isArchived' => true, + 'renewalFrequency' => 'Mock value', + 'supervisorUserId' => 'mock-id-123', ], ], ], 200), @@ -110,6 +168,24 @@ Saloon::assertSent(GetBudgetRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->budgetTypeId->toBe('mock-id-123') + ->customerId->toBe('mock-id-123') + ->showToCustomer->toBe(true) + ->changeId->toBe('mock-id-123') + ->contractId->toBe('mock-id-123') + ->title->toBe('Mock value') + ->description->toBe('Mock value') + ->totalPrice->toBe('Mock value') + ->startedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->endedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->initialMinutes->toBe(42) + ->isArchived->toBe(true) + ->renewalFrequency->toBe('Mock value') + ->supervisorUserId->toBe('mock-id-123'); }); it('calls the deleteBudget method in the Budget resource', function () { @@ -139,13 +215,13 @@ $dto->changeId = 'mock-id-123'; // todo: add every other DTO field - $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', $dto); + $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', data: $dto); Saloon::assertSent(PatchBudgetRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('budget') + ->data->type->toBe('budgets') ->data->attributes->scoped(fn ($attributes) => $attributes ->budgetTypeId->toBe('mock-id-123') ->customerId->toBe('mock-id-123') diff --git a/tests/Requests/BudgetTimeSpentTotalTest.php b/tests/Requests/BudgetTimeSpentTotalTest.php index 4e5e87a..9ec4e67 100644 --- a/tests/Requests/BudgetTimeSpentTotalTest.php +++ b/tests/Requests/BudgetTimeSpentTotalTest.php @@ -1,5 +1,6 @@ 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'start' => '2025-11-22T10:40:04.065Z', + 'end' => '2025-11-22T10:40:04.065Z', + 'remainingMinutes' => 42, + 'periodUnit' => 'Mock value', + 'periodValue' => 42, ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'start' => '2025-11-22T10:40:04.065Z', + 'end' => '2025-11-22T10:40:04.065Z', + 'remainingMinutes' => 42, + 'periodUnit' => 'Mock value', + 'periodValue' => 42, ], ], ], @@ -48,4 +57,13 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->start->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->end->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->remainingMinutes->toBe(42) + ->periodUnit->toBe('Mock value') + ->periodValue->toBe(42); }); diff --git a/tests/Requests/BudgetTypeTest.php b/tests/Requests/BudgetTypeTest.php index 61f3b79..1133cc0 100644 --- a/tests/Requests/BudgetTypeTest.php +++ b/tests/Requests/BudgetTypeTest.php @@ -16,14 +16,30 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'title' => 'Mock value', + 'isArchived' => true, + 'hasChangeTicket' => true, + 'renewalFrequencies' => 'Mock value', + 'hasSupervisor' => true, + 'hasContractId' => 'mock-id-123', + 'hasTotalPrice' => true, + 'ticketIsRequired' => true, + 'defaultTitle' => 'Mock value', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'title' => 'Mock value', + 'isArchived' => true, + 'hasChangeTicket' => true, + 'renewalFrequencies' => 'Mock value', + 'hasSupervisor' => true, + 'hasContractId' => 'mock-id-123', + 'hasTotalPrice' => true, + 'ticketIsRequired' => true, + 'defaultTitle' => 'Mock value', ], ], ], @@ -37,4 +53,17 @@ Saloon::assertSent(GetBudgetTypesRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->title->toBe('Mock value') + ->isArchived->toBe(true) + ->hasChangeTicket->toBe(true) + ->renewalFrequencies->toBe('Mock value') + ->hasSupervisor->toBe(true) + ->hasContractId->toBe('mock-id-123') + ->hasTotalPrice->toBe(true) + ->ticketIsRequired->toBe(true) + ->defaultTitle->toBe('Mock value'); }); diff --git a/tests/Requests/ChangeTest.php b/tests/Requests/ChangeTest.php index 66ea2d6..11817ba 100644 --- a/tests/Requests/ChangeTest.php +++ b/tests/Requests/ChangeTest.php @@ -29,6 +29,11 @@ Saloon::assertSent(GetChangeRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->name->toBe('Mock value'); }); it('calls the getChanges method in the Change resource', function () { @@ -60,4 +65,9 @@ Saloon::assertSent(GetChangesRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index 466bc6e..02f6207 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -27,7 +27,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('correction'); + ->data->type->toBe('corrections'); return true; }); @@ -43,13 +43,13 @@ $dto->name = 'test value'; // todo: add every other DTO field - $this->timaticConnector->correction()->patchCorrection(correctionId: 'test string', $dto); + $this->timaticConnector->correction()->patchCorrection(correctionId: 'test string', data: $dto); Saloon::assertSent(PatchCorrectionRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('correction'); + ->data->type->toBe('corrections'); return true; }); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index a2f4f55..6a06df7 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -21,14 +21,20 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'externalId' => 'mock-id-123', + 'name' => 'Mock value', + 'hourlyRate' => 'Mock value', + 'accountManagerUserId' => 'mock-id-123', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'externalId' => 'mock-id-123', + 'name' => 'Mock value', + 'hourlyRate' => 'Mock value', + 'accountManagerUserId' => 'mock-id-123', ], ], ], @@ -52,6 +58,14 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->externalId->toBe('mock-id-123') + ->name->toBe('Mock value') + ->hourlyRate->toBe('Mock value') + ->accountManagerUserId->toBe('mock-id-123'); }); it('calls the postCustomers method in the Customer resource', function () { @@ -74,7 +88,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('customer') + ->data->type->toBe('customers') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('mock-id-123') ->name->toBe('test value') @@ -93,7 +107,10 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ + 'externalId' => 'mock-id-123', 'name' => 'Mock value', + 'hourlyRate' => 'Mock value', + 'accountManagerUserId' => 'mock-id-123', ], ], ], 200), @@ -106,6 +123,14 @@ Saloon::assertSent(GetCustomerRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->externalId->toBe('mock-id-123') + ->name->toBe('Mock value') + ->hourlyRate->toBe('Mock value') + ->accountManagerUserId->toBe('mock-id-123'); }); it('calls the deleteCustomer method in the Customer resource', function () { @@ -135,13 +160,13 @@ $dto->accountManagerUserId = 'mock-id-123'; // todo: add every other DTO field - $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', $dto); + $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', data: $dto); Saloon::assertSent(PatchCustomerRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('customer') + ->data->type->toBe('customers') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('mock-id-123') ->name->toBe('test value') diff --git a/tests/Requests/DailyProgressTest.php b/tests/Requests/DailyProgressTest.php index 0ac8c78..57b20cc 100644 --- a/tests/Requests/DailyProgressTest.php +++ b/tests/Requests/DailyProgressTest.php @@ -1,5 +1,6 @@ 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'userId' => 'mock-id-123', + 'date' => '2025-11-22T10:40:04.065Z', + 'progress' => 'Mock value', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'userId' => 'mock-id-123', + 'date' => '2025-11-22T10:40:04.065Z', + 'progress' => 'Mock value', ], ], ], @@ -37,4 +42,11 @@ Saloon::assertSent(GetDailyProgressesRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->userId->toBe('mock-id-123') + ->date->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->progress->toBe('Mock value'); }); diff --git a/tests/Requests/EntriesExportTest.php b/tests/Requests/EntriesExportTest.php index 8bdba8e..55f88e8 100644 --- a/tests/Requests/EntriesExportTest.php +++ b/tests/Requests/EntriesExportTest.php @@ -96,4 +96,9 @@ Saloon::assertSent(GetBudgetEntriesExportRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index b1e633a..4429642 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -19,14 +19,28 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'ticketId' => 'mock-id-123', + 'ticketNumber' => 'Mock value', + 'customerId' => 'mock-id-123', + 'userId' => 'mock-id-123', + 'date' => 'Mock value', + 'ticketTitle' => 'Mock value', + 'ticketType' => 'Mock value', + 'budgetId' => 'mock-id-123', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'ticketId' => 'mock-id-123', + 'ticketNumber' => 'Mock value', + 'customerId' => 'mock-id-123', + 'userId' => 'mock-id-123', + 'date' => 'Mock value', + 'ticketTitle' => 'Mock value', + 'ticketType' => 'Mock value', + 'budgetId' => 'mock-id-123', ], ], ], @@ -50,6 +64,18 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->ticketId->toBe('mock-id-123') + ->ticketNumber->toBe('Mock value') + ->customerId->toBe('mock-id-123') + ->userId->toBe('mock-id-123') + ->date->toBe('Mock value') + ->ticketTitle->toBe('Mock value') + ->ticketType->toBe('Mock value') + ->budgetId->toBe('mock-id-123'); }); it('calls the getEntrySuggestion method in the EntrySuggestion resource', function () { @@ -59,7 +85,14 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'name' => 'Mock value', + 'ticketId' => 'mock-id-123', + 'ticketNumber' => 'Mock value', + 'customerId' => 'mock-id-123', + 'userId' => 'mock-id-123', + 'date' => 'Mock value', + 'ticketTitle' => 'Mock value', + 'ticketType' => 'Mock value', + 'budgetId' => 'mock-id-123', ], ], ], 200), @@ -72,6 +105,18 @@ Saloon::assertSent(GetEntrySuggestionRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->ticketId->toBe('mock-id-123') + ->ticketNumber->toBe('Mock value') + ->customerId->toBe('mock-id-123') + ->userId->toBe('mock-id-123') + ->date->toBe('Mock value') + ->ticketTitle->toBe('Mock value') + ->ticketType->toBe('Mock value') + ->budgetId->toBe('mock-id-123'); }); it('calls the deleteEntrySuggestion method in the EntrySuggestion resource', function () { diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 8301167..eae4718 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -21,14 +21,62 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'ticketId' => 'mock-id-123', + 'ticketNumber' => 'Mock value', + 'ticketTitle' => 'Mock value', + 'ticketType' => 'Mock value', + 'customerId' => 'mock-id-123', + 'customerName' => 'Mock value', + 'hourlyRate' => 'Mock value', + 'hadEmergencyShift' => true, + 'budgetId' => 'mock-id-123', + 'isPaidPerHour' => true, + 'minutesSpent' => 42, + 'userId' => 'mock-id-123', + 'userEmail' => 'test@example.com', + 'userFullName' => 'Mock value', + 'createdByUserId' => 'mock-id-123', + 'createdByUserEmail' => 'test@example.com', + 'createdByUserFullName' => 'Mock value', + 'entryType' => 'Mock value', + 'description' => 'Mock value', + 'isInternal' => true, + 'startedAt' => 'Mock value', + 'endedAt' => 'Mock value', + 'invoicedAt' => 'Mock value', + 'isInvoiced' => 'Mock value', + 'isBasedOnSuggestion' => true, ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'ticketId' => 'mock-id-123', + 'ticketNumber' => 'Mock value', + 'ticketTitle' => 'Mock value', + 'ticketType' => 'Mock value', + 'customerId' => 'mock-id-123', + 'customerName' => 'Mock value', + 'hourlyRate' => 'Mock value', + 'hadEmergencyShift' => true, + 'budgetId' => 'mock-id-123', + 'isPaidPerHour' => true, + 'minutesSpent' => 42, + 'userId' => 'mock-id-123', + 'userEmail' => 'test@example.com', + 'userFullName' => 'Mock value', + 'createdByUserId' => 'mock-id-123', + 'createdByUserEmail' => 'test@example.com', + 'createdByUserFullName' => 'Mock value', + 'entryType' => 'Mock value', + 'description' => 'Mock value', + 'isInternal' => true, + 'startedAt' => 'Mock value', + 'endedAt' => 'Mock value', + 'invoicedAt' => 'Mock value', + 'isInvoiced' => 'Mock value', + 'isBasedOnSuggestion' => true, ], ], ], @@ -56,6 +104,35 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->ticketId->toBe('mock-id-123') + ->ticketNumber->toBe('Mock value') + ->ticketTitle->toBe('Mock value') + ->ticketType->toBe('Mock value') + ->customerId->toBe('mock-id-123') + ->customerName->toBe('Mock value') + ->hourlyRate->toBe('Mock value') + ->hadEmergencyShift->toBe(true) + ->budgetId->toBe('mock-id-123') + ->isPaidPerHour->toBe(true) + ->minutesSpent->toBe(42) + ->userId->toBe('mock-id-123') + ->userEmail->toBe('test@example.com') + ->userFullName->toBe('Mock value') + ->createdByUserId->toBe('mock-id-123') + ->createdByUserEmail->toBe('test@example.com') + ->createdByUserFullName->toBe('Mock value') + ->entryType->toBe('Mock value') + ->description->toBe('Mock value') + ->isInternal->toBe(true) + ->startedAt->toBe('Mock value') + ->endedAt->toBe('Mock value') + ->invoicedAt->toBe('Mock value') + ->isInvoiced->toBe('Mock value') + ->isBasedOnSuggestion->toBe(true); }); it('calls the postEntries method in the Entry resource', function () { @@ -78,7 +155,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('entry') + ->data->type->toBe('entries') ->data->attributes->scoped(fn ($attributes) => $attributes ->ticketId->toBe('mock-id-123') ->ticketNumber->toBe('test value') @@ -97,7 +174,31 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'name' => 'Mock value', + 'ticketId' => 'mock-id-123', + 'ticketNumber' => 'Mock value', + 'ticketTitle' => 'Mock value', + 'ticketType' => 'Mock value', + 'customerId' => 'mock-id-123', + 'customerName' => 'Mock value', + 'hourlyRate' => 'Mock value', + 'hadEmergencyShift' => true, + 'budgetId' => 'mock-id-123', + 'isPaidPerHour' => true, + 'minutesSpent' => 42, + 'userId' => 'mock-id-123', + 'userEmail' => 'test@example.com', + 'userFullName' => 'Mock value', + 'createdByUserId' => 'mock-id-123', + 'createdByUserEmail' => 'test@example.com', + 'createdByUserFullName' => 'Mock value', + 'entryType' => 'Mock value', + 'description' => 'Mock value', + 'isInternal' => true, + 'startedAt' => 'Mock value', + 'endedAt' => 'Mock value', + 'invoicedAt' => 'Mock value', + 'isInvoiced' => 'Mock value', + 'isBasedOnSuggestion' => true, ], ], ], 200), @@ -110,6 +211,35 @@ Saloon::assertSent(GetEntryRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->ticketId->toBe('mock-id-123') + ->ticketNumber->toBe('Mock value') + ->ticketTitle->toBe('Mock value') + ->ticketType->toBe('Mock value') + ->customerId->toBe('mock-id-123') + ->customerName->toBe('Mock value') + ->hourlyRate->toBe('Mock value') + ->hadEmergencyShift->toBe(true) + ->budgetId->toBe('mock-id-123') + ->isPaidPerHour->toBe(true) + ->minutesSpent->toBe(42) + ->userId->toBe('mock-id-123') + ->userEmail->toBe('test@example.com') + ->userFullName->toBe('Mock value') + ->createdByUserId->toBe('mock-id-123') + ->createdByUserEmail->toBe('test@example.com') + ->createdByUserFullName->toBe('Mock value') + ->entryType->toBe('Mock value') + ->description->toBe('Mock value') + ->isInternal->toBe(true) + ->startedAt->toBe('Mock value') + ->endedAt->toBe('Mock value') + ->invoicedAt->toBe('Mock value') + ->isInvoiced->toBe('Mock value') + ->isBasedOnSuggestion->toBe(true); }); it('calls the deleteEntry method in the Entry resource', function () { @@ -139,13 +269,13 @@ $dto->ticketType = 'test value'; // todo: add every other DTO field - $this->timaticConnector->entry()->patchEntry(entryId: 'test string', $dto); + $this->timaticConnector->entry()->patchEntry(entryId: 'test string', data: $dto); Saloon::assertSent(PatchEntryRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('entry') + ->data->type->toBe('entries') ->data->attributes->scoped(fn ($attributes) => $attributes ->ticketId->toBe('mock-id-123') ->ticketNumber->toBe('test value') diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index 0c1a444..d4efbb7 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -29,7 +29,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('event') + ->data->type->toBe('events') ->data->attributes->scoped(fn ($attributes) => $attributes ->userId->toBe('mock-id-123') ->budgetId->toBe('mock-id-123') diff --git a/tests/Requests/ExportMailTest.php b/tests/Requests/ExportMailTest.php index 925515f..58ff867 100644 --- a/tests/Requests/ExportMailTest.php +++ b/tests/Requests/ExportMailTest.php @@ -37,4 +37,9 @@ Saloon::assertSent(GetBudgetsExportMailsRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/IncidentTest.php b/tests/Requests/IncidentTest.php index ec2cfca..f59ea36 100644 --- a/tests/Requests/IncidentTest.php +++ b/tests/Requests/IncidentTest.php @@ -29,6 +29,11 @@ Saloon::assertSent(GetIncidentRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->name->toBe('Mock value'); }); it('calls the getIncidents method in the Incident resource', function () { @@ -60,4 +65,9 @@ Saloon::assertSent(GetIncidentsRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index 2d5eb41..a6ae76d 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -22,14 +22,14 @@ $dto->endedAt = 'test value'; // todo: add every other DTO field - $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', $dto); + $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', data: $dto); Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('markAsExported') + ->data->type->toBe('markAsExporteds') ->data->attributes->scoped(fn ($attributes) => $attributes ->entryId->toBe('mock-id-123') ->overtimeTypeId->toBe('mock-id-123') diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index 2aaa277..dbfb1ce 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -22,14 +22,14 @@ $dto->ticketType = 'test value'; // todo: add every other DTO field - $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced(entryId: 'test string', $dto); + $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced(entryId: 'test string', data: $dto); Saloon::assertSent(PostEntryMarkAsInvoicedRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('markAsInvoiced') + ->data->type->toBe('markAsInvoiceds') ->data->attributes->scoped(fn ($attributes) => $attributes ->ticketId->toBe('mock-id-123') ->ticketNumber->toBe('test value') diff --git a/tests/Requests/MeTest.php b/tests/Requests/MeTest.php index 0ded68f..dc8b6ff 100644 --- a/tests/Requests/MeTest.php +++ b/tests/Requests/MeTest.php @@ -37,4 +37,9 @@ Saloon::assertSent(GetMesRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/NumberTest.php b/tests/Requests/NumberTest.php index 96e6534..933e336 100644 --- a/tests/Requests/NumberTest.php +++ b/tests/Requests/NumberTest.php @@ -28,4 +28,9 @@ Saloon::assertSent(GetIncidentsNumberRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index 35d3b9d..0b57208 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -17,14 +17,28 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'entryId' => 'mock-id-123', + 'overtimeTypeId' => 'mock-id-123', + 'startedAt' => 'Mock value', + 'endedAt' => 'Mock value', + 'percentages' => 'Mock value', + 'approvedAt' => 'Mock value', + 'approvedByUserId' => 'mock-id-123', + 'exportedAt' => 'Mock value', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'entryId' => 'mock-id-123', + 'overtimeTypeId' => 'mock-id-123', + 'startedAt' => 'Mock value', + 'endedAt' => 'Mock value', + 'percentages' => 'Mock value', + 'approvedAt' => 'Mock value', + 'approvedByUserId' => 'mock-id-123', + 'exportedAt' => 'Mock value', ], ], ], @@ -52,4 +66,16 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->entryId->toBe('mock-id-123') + ->overtimeTypeId->toBe('mock-id-123') + ->startedAt->toBe('Mock value') + ->endedAt->toBe('Mock value') + ->percentages->toBe('Mock value') + ->approvedAt->toBe('Mock value') + ->approvedByUserId->toBe('mock-id-123') + ->exportedAt->toBe('Mock value'); }); diff --git a/tests/Requests/PeriodTest.php b/tests/Requests/PeriodTest.php index f69f057..6e4938e 100644 --- a/tests/Requests/PeriodTest.php +++ b/tests/Requests/PeriodTest.php @@ -28,4 +28,9 @@ Saloon::assertSent(GetBudgetPeriodsRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->name->toBe('Mock value'); }); diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index 75ac031..397e647 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -21,14 +21,16 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'externalId' => 'mock-id-123', + 'name' => 'Mock value', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'externalId' => 'mock-id-123', + 'name' => 'Mock value', ], ], ], @@ -42,6 +44,12 @@ Saloon::assertSent(GetTeamsRequest::class); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->externalId->toBe('mock-id-123') + ->name->toBe('Mock value'); }); it('calls the postTeams method in the Team resource', function () { @@ -62,7 +70,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('team') + ->data->type->toBe('teams') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('mock-id-123') ->name->toBe('test value') @@ -79,6 +87,7 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ + 'externalId' => 'mock-id-123', 'name' => 'Mock value', ], ], @@ -92,6 +101,12 @@ Saloon::assertSent(GetTeamRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->externalId->toBe('mock-id-123') + ->name->toBe('Mock value'); }); it('calls the deleteTeam method in the Team resource', function () { @@ -119,13 +134,13 @@ $dto->name = 'test value'; // todo: add every other DTO field - $this->timaticConnector->team()->patchTeam(teamId: 'test string', $dto); + $this->timaticConnector->team()->patchTeam(teamId: 'test string', data: $dto); Saloon::assertSent(PatchTeamRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('team') + ->data->type->toBe('teams') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('mock-id-123') ->name->toBe('test value') diff --git a/tests/Requests/TimeSpentTotalTest.php b/tests/Requests/TimeSpentTotalTest.php index 7464411..490fd29 100644 --- a/tests/Requests/TimeSpentTotalTest.php +++ b/tests/Requests/TimeSpentTotalTest.php @@ -1,5 +1,6 @@ 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'start' => '2025-11-22T10:40:04.065Z', + 'end' => '2025-11-22T10:40:04.065Z', + 'internalMinutes' => 42, + 'billableMinutes' => 42, + 'periodUnit' => 'Mock value', + 'periodValue' => 42, ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'start' => '2025-11-22T10:40:04.065Z', + 'end' => '2025-11-22T10:40:04.065Z', + 'internalMinutes' => 42, + 'billableMinutes' => 42, + 'periodUnit' => 'Mock value', + 'periodValue' => 42, ], ], ], @@ -50,4 +61,14 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->start->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->end->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->internalMinutes->toBe(42) + ->billableMinutes->toBe(42) + ->periodUnit->toBe('Mock value') + ->periodValue->toBe(42); }); diff --git a/tests/Requests/UserCustomerHoursAggregateTest.php b/tests/Requests/UserCustomerHoursAggregateTest.php index e46b050..cd55105 100644 --- a/tests/Requests/UserCustomerHoursAggregateTest.php +++ b/tests/Requests/UserCustomerHoursAggregateTest.php @@ -17,14 +17,22 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'data' => [], + 'customerId' => 'mock-id-123', + 'userId' => 'mock-id-123', + 'internalMinutes' => 42, + 'budgetMinutes' => 42, + 'paidPerHourMinutes' => 42, ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'data' => [], + 'customerId' => 'mock-id-123', + 'userId' => 'mock-id-123', + 'internalMinutes' => 42, + 'budgetMinutes' => 42, + 'paidPerHourMinutes' => 42, ], ], ], @@ -52,4 +60,13 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->customerId->toBe('mock-id-123') + ->userId->toBe('mock-id-123') + ->internalMinutes->toBe(42) + ->budgetMinutes->toBe(42) + ->paidPerHourMinutes->toBe(42); }); diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index d72b2fb..c05d0ee 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -21,14 +21,16 @@ 'type' => 'resources', 'id' => 'mock-id-1', 'attributes' => [ - 'name' => 'Mock value', + 'externalId' => 'mock-id-123', + 'email' => 'test@example.com', ], ], 1 => [ 'type' => 'resources', 'id' => 'mock-id-2', 'attributes' => [ - 'name' => 'Mock value', + 'externalId' => 'mock-id-123', + 'email' => 'test@example.com', ], ], ], @@ -52,6 +54,12 @@ }); expect($response->status())->toBe(200); + + $dtoCollection = $response->dto(); + + expect($dtoCollection->first()) + ->externalId->toBe('mock-id-123') + ->email->toBe('test@example.com'); }); it('calls the postUsers method in the User resource', function () { @@ -72,7 +80,7 @@ expect($request->body()->all()) ->toHaveKey('data') // POST calls dont have an ID field - ->data->type->toBe('user') + ->data->type->toBe('users') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('mock-id-123') ->email->toBe('test@example.com') @@ -89,7 +97,8 @@ 'type' => 'resources', 'id' => 'mock-id-123', 'attributes' => [ - 'name' => 'Mock value', + 'externalId' => 'mock-id-123', + 'email' => 'test@example.com', ], ], ], 200), @@ -102,6 +111,12 @@ Saloon::assertSent(GetUserRequest::class); expect($response->status())->toBe(200); + + $dto = $response->dto(); + + expect($dto) + ->externalId->toBe('mock-id-123') + ->email->toBe('test@example.com'); }); it('calls the deleteUser method in the User resource', function () { @@ -129,13 +144,13 @@ $dto->email = 'test@example.com'; // todo: add every other DTO field - $this->timaticConnector->user()->patchUser(userId: 'test string', $dto); + $this->timaticConnector->user()->patchUser(userId: 'test string', data: $dto); Saloon::assertSent(PatchUserRequest::class); $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - ->data->type->toBe('user') + ->data->type->toBe('users') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('mock-id-123') ->email->toBe('test@example.com') From b56da25d3d9799b0ab58cdab28aefdb0cd166fe6 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 22:15:06 +0100 Subject: [PATCH 40/62] fix type for fields hasSomeID field --- .../TestGenerators/Traits/DtoAssertions.php | 44 ++++++++++++------- .../Traits/MockDataGeneratorTrait.php | 44 ++++++++++++------- tests/Requests/BudgetTypeTest.php | 6 +-- 3 files changed, 61 insertions(+), 33 deletions(-) diff --git a/generator/TestGenerators/Traits/DtoAssertions.php b/generator/TestGenerators/Traits/DtoAssertions.php index 1032181..d7cdeb0 100644 --- a/generator/TestGenerators/Traits/DtoAssertions.php +++ b/generator/TestGenerators/Traits/DtoAssertions.php @@ -137,25 +137,39 @@ protected function generateMockValueForDtoProperty(string $propertyName, ?string return '2025-11-22T10:40:04.065Z'; } - // ID fields - if (str_ends_with($propertyName, 'Id')) { - return 'mock-id-123'; + // Type-based generation (handle explicit types first) + if ($typeName === 'bool') { + return true; } - // Email fields - if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { - return 'test@example.com'; + if ($typeName === 'int') { + return 42; } - // Type-based generation - return match ($typeName) { - 'bool' => true, - 'int' => 42, - 'float' => 3.14, - 'string' => 'Mock value', - 'array' => [], - default => 'Mock value', - }; + if ($typeName === 'float') { + return 3.14; + } + + if ($typeName === 'array') { + return []; + } + + // String type - apply name-based heuristics + if ($typeName === 'string') { + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return 'mock-id-123'; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return 'test@example.com'; + } + + return 'Mock value'; + } + + return 'Mock value'; } /** diff --git a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php index 7550ebe..2efcc96 100644 --- a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php +++ b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php @@ -92,25 +92,39 @@ protected function getMockValueForPropertySchema(string $propertyName, Schema $p return '2025-01-15T10:30:00Z'; } - // ID fields - if (str_ends_with($propertyName, 'Id')) { - return 'mock-id-123'; + // Type-based generation (handle explicit types first) + if ($type === 'boolean') { + return true; } - // Email fields - if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { - return 'test@example.com'; + if ($type === 'integer' || $type === 'number') { + return 42; } - // Type-based generation - return match ($type) { - 'boolean' => true, - 'integer', 'number' => 42, - 'string' => 'Mock value', - 'array' => [], - 'object' => [], - default => 'Mock value', - }; + if ($type === 'array') { + return []; + } + + if ($type === 'object') { + return []; + } + + // String type - apply name-based heuristics + if ($type === 'string') { + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return 'mock-id-123'; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return 'test@example.com'; + } + + return 'Mock value'; + } + + return 'Mock value'; } /** diff --git a/tests/Requests/BudgetTypeTest.php b/tests/Requests/BudgetTypeTest.php index 1133cc0..17a6991 100644 --- a/tests/Requests/BudgetTypeTest.php +++ b/tests/Requests/BudgetTypeTest.php @@ -21,7 +21,7 @@ 'hasChangeTicket' => true, 'renewalFrequencies' => 'Mock value', 'hasSupervisor' => true, - 'hasContractId' => 'mock-id-123', + 'hasContractId' => true, 'hasTotalPrice' => true, 'ticketIsRequired' => true, 'defaultTitle' => 'Mock value', @@ -36,7 +36,7 @@ 'hasChangeTicket' => true, 'renewalFrequencies' => 'Mock value', 'hasSupervisor' => true, - 'hasContractId' => 'mock-id-123', + 'hasContractId' => true, 'hasTotalPrice' => true, 'ticketIsRequired' => true, 'defaultTitle' => 'Mock value', @@ -62,7 +62,7 @@ ->hasChangeTicket->toBe(true) ->renewalFrequencies->toBe('Mock value') ->hasSupervisor->toBe(true) - ->hasContractId->toBe('mock-id-123') + ->hasContractId->toBe(true) ->hasTotalPrice->toBe(true) ->ticketIsRequired->toBe(true) ->defaultTitle->toBe('Mock value'); From 8912618ccb26759fb448be69886cbcc729ed7fab Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sat, 22 Nov 2025 23:57:40 +0100 Subject: [PATCH 41/62] skip tests for endpoints without DTO --- claude-todo.md | 35 --------- generator/JsonApiPestTestGenerator.php | 51 +++++++++++- tests/Requests/ChangeTest.php | 73 ----------------- tests/Requests/EntriesExportTest.php | 104 ------------------------- tests/Requests/IncidentTest.php | 73 ----------------- tests/Requests/MeTest.php | 45 ----------- tests/Requests/NumberTest.php | 36 --------- tests/Requests/PeriodTest.php | 36 --------- 8 files changed, 49 insertions(+), 404 deletions(-) delete mode 100644 tests/Requests/ChangeTest.php delete mode 100644 tests/Requests/EntriesExportTest.php delete mode 100644 tests/Requests/IncidentTest.php delete mode 100644 tests/Requests/MeTest.php delete mode 100644 tests/Requests/NumberTest.php delete mode 100644 tests/Requests/PeriodTest.php diff --git a/claude-todo.md b/claude-todo.md index 4e4cb37..cfa5477 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -1,40 +1,5 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. -## Task: apply Hydration for Requests - -- Add `protected $model = Budget::class;` to the BudgetClass for example -- All GET, POST, PATCH request need to implement createDtoFromResponse -``` -function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrateCollection( - $this->model, - $response->json('data'), - $response->json('included') - ); - } -``` - -## Task 3: Validate DTO Hydration in GET Tests - -**Goal:** Verify JSON:API responses properly populate DTOs - -Test the fields that are set in the json to be present on the DTO. -``` - $dtoCollection = $response->dto(); - - expect($dtoCollection->first()) - ->budgetTypeId->toBe("budget-type-xyz") - ->customerId->toBe("customer-abc") - ->showToCustomer->toBe(true) - ->startedAt->toEqual(new Carbon("2025-11-22T10:40:04.065Z")) -``` - -**Changes:** -- Add expectations after response: `expect($data['type'])->toBe('entries')` -- Check DTO properties are present with expected values -- Regenerate with `composer regenerate` and test with `composer test` - ## TASK: fix the test that have ## TASK: Remove Me Request, Me Resource and Me Test diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 2190925..62b168d 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -27,6 +27,8 @@ class JsonApiPestTestGenerator extends PestTestGenerator protected DeleteRequestTestGenerator $deleteTestGenerator; + protected GeneratedCode $generatedCode; + /** * Override process() to instantiate test generators with ApiSpecification and GeneratedCode */ @@ -35,6 +37,9 @@ public function process( ApiSpecification $specification, GeneratedCode $generatedCode, ): PhpFile|array|null { + // Store generated code for later use + $this->generatedCode = $generatedCode; + // Instantiate test generators with the parsed ApiSpecification and GeneratedCode $this->collectionTestGenerator = new CollectionRequestTestGenerator($specification, $generatedCode); $this->mutationTestGenerator = new MutationRequestTestGenerator($specification, $generatedCode); @@ -62,11 +67,53 @@ protected function shouldGenerateTestCaseFile(): bool } /** - * Filter out PUT requests - not supported in JSON:API + * Filter out PUT requests and endpoints without DTOs */ protected function shouldIncludeEndpoint(Endpoint $endpoint): bool { - return ! $endpoint->method->isPut(); + if ($endpoint->method->isPut()) { + return false; + } + + // Skip endpoints without DTOs (endpoints that don't return data) + if (! $this->hasDtoForEndpoint($endpoint)) { + return false; + } + + return true; + } + + /** + * Check if a DTO exists for the endpoint + */ + protected function hasDtoForEndpoint(Endpoint $endpoint): bool + { + $dtoClassName = $this->getDtoClassName($endpoint); + + // Check if this DTO was generated in the current run + return array_key_exists($dtoClassName, $this->generatedCode->dtoClasses); + } + + /** + * Get DTO class name from endpoint + */ + protected function getDtoClassName(Endpoint $endpoint): string + { + // Use collection name to determine DTO + if ($endpoint->collection) { + $resourceName = NameHelper::resourceClassName($endpoint->collection); + + // Use Laravel's Str::singular() for correct singular form + return \Illuminate\Support\Str::singular($resourceName); + } + + // Fallback: try to parse from endpoint name + $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); + // Remove method prefix (post, patch, get) + $name = preg_replace('/^(post|patch|get)/i', '', $name); + + // Use Laravel's Str::singular() for correct singular form + return \Illuminate\Support\Str::singular(NameHelper::resourceClassName($name)); } /** diff --git a/tests/Requests/ChangeTest.php b/tests/Requests/ChangeTest.php deleted file mode 100644 index 11817ba..0000000 --- a/tests/Requests/ChangeTest.php +++ /dev/null @@ -1,73 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getChange method in the Change resource', function () { - Saloon::fake([ - GetChangeRequest::class => MockResponse::make([ - 'data' => [ - 'type' => 'resources', - 'id' => 'mock-id-123', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], 200), - ]); - - $response = $this->timaticConnector->change()->getChange( - changeId: 'test string' - ); - - Saloon::assertSent(GetChangeRequest::class); - - expect($response->status())->toBe(200); - - $dto = $response->dto(); - - expect($dto) - ->name->toBe('Mock value'); -}); - -it('calls the getChanges method in the Change resource', function () { - Saloon::fake([ - GetChangesRequest::class => MockResponse::make([ - 'data' => [ - 0 => [ - 'type' => 'resources', - 'id' => 'mock-id-1', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - 1 => [ - 'type' => 'resources', - 'id' => 'mock-id-2', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], - ], 200), - ]); - - $request = (new GetChangesRequest); - - $response = $this->timaticConnector->send($request); - - Saloon::assertSent(GetChangesRequest::class); - - expect($response->status())->toBe(200); - - $dtoCollection = $response->dto(); - - expect($dtoCollection->first()) - ->name->toBe('Mock value'); -}); diff --git a/tests/Requests/EntriesExportTest.php b/tests/Requests/EntriesExportTest.php deleted file mode 100644 index 55f88e8..0000000 --- a/tests/Requests/EntriesExportTest.php +++ /dev/null @@ -1,104 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getBudgetEntriesExport method in the EntriesExport resource', function () { - Saloon::fake([ - GetBudgetEntriesExportRequest::class => MockResponse::make([ - 'data' => [ - 'type' => 'resources', - 'id' => 'mock-id-123', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], 200), - ]); - - $response = $this->timaticConnector->entriesExport()->getBudgetEntriesExport( - budgetId: 'test string', - filteruserId: 'test string', - filteruserIdeq: 'test string', - filteruserIdnq: 'test string', - filteruserIdgt: 'test string', - filteruserIdlt: 'test string', - filteruserIdgte: 'test string', - filteruserIdlte: 'test string', - filteruserIdcontains: 'test string', - filterbudgetId: 'test string', - filterbudgetIdeq: 'test string', - filterbudgetIdnq: 'test string', - filterbudgetIdgt: 'test string', - filterbudgetIdlt: 'test string', - filterbudgetIdgte: 'test string', - filterbudgetIdlte: 'test string', - filterbudgetIdcontains: 'test string', - filterstartedAt: 'test string', - filterstartedAteq: 'test string', - filterstartedAtnq: 'test string', - filterstartedAtgt: 'test string', - filterstartedAtlt: 'test string', - filterstartedAtgte: 'test string', - filterstartedAtlte: 'test string', - filterstartedAtcontains: 'test string', - filterendedAt: 'test string', - filterendedAteq: 'test string', - filterendedAtnq: 'test string', - filterendedAtgt: 'test string', - filterendedAtlt: 'test string', - filterendedAtgte: 'test string', - filterendedAtlte: 'test string', - filterendedAtcontains: 'test string', - filterhasOvertime: 'test string', - filterhasOvertimeeq: 'test string', - filterhasOvertimenq: 'test string', - filterhasOvertimegt: 'test string', - filterhasOvertimelt: 'test string', - filterhasOvertimegte: 'test string', - filterhasOvertimelte: 'test string', - filterhasOvertimecontains: 'test string', - filteruserFullName: 'test string', - filteruserFullNameeq: 'test string', - filteruserFullNamenq: 'test string', - filteruserFullNamegt: 'test string', - filteruserFullNamelt: 'test string', - filteruserFullNamegte: 'test string', - filteruserFullNamelte: 'test string', - filteruserFullNamecontains: 'test string', - filtercustomerId: 'test string', - filtercustomerIdeq: 'test string', - filtercustomerIdnq: 'test string', - filtercustomerIdgt: 'test string', - filtercustomerIdlt: 'test string', - filtercustomerIdgte: 'test string', - filtercustomerIdlte: 'test string', - filtercustomerIdcontains: 'test string', - filterticketNumber: 'test string', - filterticketNumbereq: 'test string', - filterticketNumbernq: 'test string', - filterticketNumbergt: 'test string', - filterticketNumberlt: 'test string', - filterticketNumbergte: 'test string', - filterticketNumberlte: 'test string', - filterticketNumbercontains: 'test string', - filtersettlement: 'test string', - filterisInvoiced: 'test string', - filterisInvoiceable: 'test string', - include: 'test string' - ); - - Saloon::assertSent(GetBudgetEntriesExportRequest::class); - - expect($response->status())->toBe(200); - - $dto = $response->dto(); - - expect($dto) - ->name->toBe('Mock value'); -}); diff --git a/tests/Requests/IncidentTest.php b/tests/Requests/IncidentTest.php deleted file mode 100644 index f59ea36..0000000 --- a/tests/Requests/IncidentTest.php +++ /dev/null @@ -1,73 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getIncident method in the Incident resource', function () { - Saloon::fake([ - GetIncidentRequest::class => MockResponse::make([ - 'data' => [ - 'type' => 'resources', - 'id' => 'mock-id-123', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], 200), - ]); - - $response = $this->timaticConnector->incident()->getIncident( - incidentId: 'test string' - ); - - Saloon::assertSent(GetIncidentRequest::class); - - expect($response->status())->toBe(200); - - $dto = $response->dto(); - - expect($dto) - ->name->toBe('Mock value'); -}); - -it('calls the getIncidents method in the Incident resource', function () { - Saloon::fake([ - GetIncidentsRequest::class => MockResponse::make([ - 'data' => [ - 0 => [ - 'type' => 'resources', - 'id' => 'mock-id-1', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - 1 => [ - 'type' => 'resources', - 'id' => 'mock-id-2', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], - ], 200), - ]); - - $request = (new GetIncidentsRequest); - - $response = $this->timaticConnector->send($request); - - Saloon::assertSent(GetIncidentsRequest::class); - - expect($response->status())->toBe(200); - - $dtoCollection = $response->dto(); - - expect($dtoCollection->first()) - ->name->toBe('Mock value'); -}); diff --git a/tests/Requests/MeTest.php b/tests/Requests/MeTest.php deleted file mode 100644 index dc8b6ff..0000000 --- a/tests/Requests/MeTest.php +++ /dev/null @@ -1,45 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getMes method in the Me resource', function () { - Saloon::fake([ - GetMesRequest::class => MockResponse::make([ - 'data' => [ - 0 => [ - 'type' => 'resources', - 'id' => 'mock-id-1', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - 1 => [ - 'type' => 'resources', - 'id' => 'mock-id-2', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], - ], 200), - ]); - - $request = (new GetMesRequest); - - $response = $this->timaticConnector->send($request); - - Saloon::assertSent(GetMesRequest::class); - - expect($response->status())->toBe(200); - - $dtoCollection = $response->dto(); - - expect($dtoCollection->first()) - ->name->toBe('Mock value'); -}); diff --git a/tests/Requests/NumberTest.php b/tests/Requests/NumberTest.php deleted file mode 100644 index 933e336..0000000 --- a/tests/Requests/NumberTest.php +++ /dev/null @@ -1,36 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getIncidentsNumber method in the Number resource', function () { - Saloon::fake([ - GetIncidentsNumberRequest::class => MockResponse::make([ - 'data' => [ - 'type' => 'resources', - 'id' => 'mock-id-123', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], 200), - ]); - - $response = $this->timaticConnector->number()->getIncidentsNumber( - incidentId: 'test string' - ); - - Saloon::assertSent(GetIncidentsNumberRequest::class); - - expect($response->status())->toBe(200); - - $dto = $response->dto(); - - expect($dto) - ->name->toBe('Mock value'); -}); diff --git a/tests/Requests/PeriodTest.php b/tests/Requests/PeriodTest.php deleted file mode 100644 index 6e4938e..0000000 --- a/tests/Requests/PeriodTest.php +++ /dev/null @@ -1,36 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getBudgetPeriods method in the Period resource', function () { - Saloon::fake([ - GetBudgetPeriodsRequest::class => MockResponse::make([ - 'data' => [ - 'type' => 'resources', - 'id' => 'mock-id-123', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], 200), - ]); - - $response = $this->timaticConnector->period()->getBudgetPeriods( - budgetId: 'test string' - ); - - Saloon::assertSent(GetBudgetPeriodsRequest::class); - - expect($response->status())->toBe(200); - - $dto = $response->dto(); - - expect($dto) - ->name->toBe('Mock value'); -}); From 22905d9c005e05515dc4a9b51f4f15cbef68c2c2 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 00:27:08 +0100 Subject: [PATCH 42/62] deduplicate test data generation --- claude-todo.md | 4 - .../CollectionRequestTestGenerator.php | 8 +- .../MutationRequestTestGenerator.php | 46 +-- .../SingularGetRequestTestGenerator.php | 4 +- .../Traits/MockDataGeneratorTrait.php | 181 ----------- .../Traits/TestDataGeneratorTrait.php | 289 ++++++++++++++++++ .../Traits/TestValueGeneratorTrait.php | 89 ------ tests/Requests/ApproveTest.php | 16 +- tests/Requests/BudgetTest.php | 36 +-- tests/Requests/BudgetTimeSpentTotalTest.php | 4 +- tests/Requests/CustomerTest.php | 28 +- tests/Requests/EntryTest.php | 20 +- tests/Requests/EventTest.php | 16 +- tests/Requests/MarkAsExportedTest.php | 16 +- tests/Requests/MarkAsInvoicedTest.php | 4 +- tests/Requests/OvertimeTest.php | 12 +- tests/Requests/TeamTest.php | 16 +- tests/Requests/TimeSpentTotalTest.php | 8 +- .../UserCustomerHoursAggregateTest.php | 12 +- tests/Requests/UserTest.php | 12 +- 20 files changed, 398 insertions(+), 423 deletions(-) delete mode 100644 generator/TestGenerators/Traits/MockDataGeneratorTrait.php create mode 100644 generator/TestGenerators/Traits/TestDataGeneratorTrait.php delete mode 100644 generator/TestGenerators/Traits/TestValueGeneratorTrait.php diff --git a/claude-todo.md b/claude-todo.md index cfa5477..635dc93 100644 --- a/claude-todo.md +++ b/claude-todo.md @@ -1,9 +1,5 @@ Focus steeds op een enkele hoofdtaak. Als deze klaar is geef dan de gelegenheid om feedback te geven en de resultaten te committen. -## TASK: fix the test that have - -## TASK: Remove Me Request, Me Resource and Me Test - ## TASK: replace "type: resource" with actual resource in mocked GET data ## Gebruik raw JSON alleen om operationId → schema reference mapping te vinden (totdat we vendor package kunnen patchen) diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 73c45ba..3b32f1e 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -10,16 +10,14 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; -use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class CollectionRequestTestGenerator { use DtoAssertions; - use MockDataGeneratorTrait; use SchemaExtractorTrait; - use TestValueGeneratorTrait; + use TestDataGeneratorTrait; protected ApiSpecification $specification; @@ -202,7 +200,7 @@ protected function generateFilterChainWithData(Endpoint $endpoint): array // Only add filters without operators (simpler) if (! $operator && count($filters) < $maxFilters) { - $value = $this->generateTestValueForProperty($property); + $value = $this->formatAsCode($this->generateValue($property)); $filters[] = "->filter('{$property}', {$value})"; // Generate assertion for this filter diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 1517d28..6e8504f 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -11,12 +11,12 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\TestValueGeneratorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class MutationRequestTestGenerator { use SchemaExtractorTrait; - use TestValueGeneratorTrait; + use TestDataGeneratorTrait; protected ApiSpecification $specification; @@ -135,7 +135,7 @@ protected function generateDtoProperties(Endpoint $endpoint): string continue; } - $value = $this->generateTestValueForProperty($propName, $propInfo['type']); + $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); $lines[] = " \$dto->{$propName} = {$value};"; $count++; } @@ -198,44 +198,6 @@ protected function getDtoPropertiesFromGeneratedCode(string $dtoClassName): arra return $properties; } - /** - * Generate test value for a DTO property - */ - protected function generateTestValueForProperty(string $propertyName, ?string $typeName): string - { - if (! $typeName) { - return "'test value'"; - } - - // Normalize type name (remove nullable prefix) - $typeName = ltrim($typeName, '?'); - - // DateTime fields - if (str_contains($typeName, 'Carbon') || str_contains($typeName, 'DateTime')) { - return "'2025-01-15T10:30:00Z'"; - } - - // ID fields - if (str_ends_with($propertyName, 'Id')) { - return "'mock-id-123'"; - } - - // Email fields - if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { - return "'test@example.com'"; - } - - // Type-based generation - return match ($typeName) { - 'bool' => 'true', - 'int' => '42', - 'float' => '3.14', - 'string' => "'test value'", - 'array' => '[]', - default => "'test value'", - }; - } - /** * Generate body validation code */ @@ -302,7 +264,7 @@ protected function generateAttributeValidationsFromDto(array $properties): strin continue; } - $value = $this->generateTestValueForProperty($propName, $propInfo['type']); + $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); $assertionValue = $this->formatValueForAssertion($value); $lines[] = " ->{$propName}->toBe({$assertionValue})"; $count++; diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index d74bc9d..cf36088 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -10,14 +10,14 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; -use Timatic\SDK\Generator\TestGenerators\Traits\MockDataGeneratorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class SingularGetRequestTestGenerator { use DtoAssertions; - use MockDataGeneratorTrait; use SchemaExtractorTrait; + use TestDataGeneratorTrait; protected ApiSpecification $specification; diff --git a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php b/generator/TestGenerators/Traits/MockDataGeneratorTrait.php deleted file mode 100644 index 2efcc96..0000000 --- a/generator/TestGenerators/Traits/MockDataGeneratorTrait.php +++ /dev/null @@ -1,181 +0,0 @@ -findOperationByEndpoint($endpoint); - if (! $operation) { - return null; - } - - // Get the 200 response - $response = $operation->responses['200'] ?? $operation->responses[200] ?? null; - if (! $response) { - return null; - } - - // Get JSON content - $mediaType = $response->content['application/json'] ?? null; - if (! $mediaType) { - return null; - } - - // Try to find examples in this order: - // 1. MediaType-level example - if ($mediaType->example !== null) { - return $mediaType->example; - } - - // 2. MediaType-level examples array (use first one) - if ($mediaType->examples && is_array($mediaType->examples)) { - $firstExample = reset($mediaType->examples); - if ($firstExample && isset($firstExample->value)) { - return $firstExample->value; - } - } - - // 3. Schema-level example - if ($mediaType->schema && $mediaType->schema->example !== null) { - return $mediaType->schema->example; - } - - return null; - } - - /** - * Generate mock attributes based on schema properties - */ - protected function generateMockAttributes(Schema $schema): array - { - $attributes = []; - - // Extract the actual properties from the schema - $properties = $this->extractPropertiesFromSchema($schema); - - if (empty($properties)) { - return ['name' => 'Mock value']; - } - - foreach ($properties as $propName => $propSchema) { - $attributes[$propName] = $this->getMockValueForPropertySchema($propName, $propSchema); - } - - return $attributes; - } - - /** - * Generate a mock value based on property name and Schema - */ - protected function getMockValueForPropertySchema(string $propertyName, Schema $propertySchema): mixed - { - // Check for example in property schema - if ($propertySchema->example !== null) { - return $propertySchema->example; - } - - $type = $propertySchema->type ?? 'string'; - $format = $propertySchema->format ?? null; - - // DateTime fields - if ($format === 'date-time' || str_contains($propertyName, 'At') || str_contains($propertyName, 'Date')) { - return '2025-01-15T10:30:00Z'; - } - - // Type-based generation (handle explicit types first) - if ($type === 'boolean') { - return true; - } - - if ($type === 'integer' || $type === 'number') { - return 42; - } - - if ($type === 'array') { - return []; - } - - if ($type === 'object') { - return []; - } - - // String type - apply name-based heuristics - if ($type === 'string') { - // ID fields - if (str_ends_with($propertyName, 'Id')) { - return 'mock-id-123'; - } - - // Email fields - if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { - return 'test@example.com'; - } - - return 'Mock value'; - } - - return 'Mock value'; - } - - /** - * Generate the complete mock response body for an endpoint - */ - protected function generateMockResponseBody(Endpoint $endpoint): string - { - // Try to extract example from OpenAPI spec first - $example = $this->extractExampleFromSpec($endpoint); - - if ($example !== null) { - // Use example from spec - $mockData = $example; - } else { - // Generate fallback mock data - $mockData = $this->generateMockData($endpoint); - } - - // Format as PHP array syntax for the stub - return $this->formatArrayAsPhp($mockData); - } - - /** - * Format an array as PHP code string - */ - protected function formatArrayAsPhp(array $data, int $indent = 0): string - { - $indentStr = str_repeat(' ', $indent); - $lines = []; - - foreach ($data as $key => $value) { - $keyStr = is_string($key) ? "'$key'" : $key; - - if (is_array($value)) { - $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; - } elseif (is_string($value)) { - $escapedValue = addslashes($value); - $lines[] = $indentStr." $keyStr => '$escapedValue',"; - } elseif (is_bool($value)) { - $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; - } elseif (is_null($value)) { - $lines[] = $indentStr." $keyStr => null,"; - } else { - $lines[] = $indentStr." $keyStr => $value,"; - } - } - - if (empty($lines)) { - return '[]'; - } - - return "[\n".implode("\n", $lines)."\n$indentStr]"; - } -} diff --git a/generator/TestGenerators/Traits/TestDataGeneratorTrait.php b/generator/TestGenerators/Traits/TestDataGeneratorTrait.php new file mode 100644 index 0000000..75c153b --- /dev/null +++ b/generator/TestGenerators/Traits/TestDataGeneratorTrait.php @@ -0,0 +1,289 @@ +extractTypeInfo($typeInfo); + + // Use example if available + if ($example !== null) { + return $example; + } + + // DateTime fields (by format or name) + if ($format === 'date-time' || str_contains($propertyName, 'At') || str_contains($propertyName, 'Date')) { + return '2025-01-15T10:30:00Z'; + } + + // ID fields + if (str_ends_with($propertyName, 'Id')) { + return Str::snake($propertyName).'-123'; + } + + // Email fields + if (str_contains($propertyName, 'email') || str_contains($propertyName, 'Email')) { + return 'test@example.com'; + } + + // Boolean fields (by type or name prefix) + if ($type === 'boolean' || $type === 'bool' || str_starts_with($propertyName, 'is') || str_starts_with($propertyName, 'has')) { + return true; + } + + // Numeric fields + if ($type === 'integer' || $type === 'int') { + return 42; + } + + if ($type === 'number' || $type === 'float') { + return 3.14; + } + + // Array/Object fields + if ($type === 'array' || $type === 'object') { + return []; + } + + // Common string property names + if ($type === 'string' || $type === null) { + if ($propertyName === 'title') { + return 'test title'; + } + if ($propertyName === 'description') { + return 'test description'; + } + if ($propertyName === 'name') { + return 'test name'; + } + + return 'test value'; + } + + return 'test value'; + } + + /** + * Format a value as PHP code string for test generation + * + * @param mixed $value The value to format + * @return string PHP code representation + */ + protected function formatAsCode(mixed $value): string + { + if (is_string($value)) { + $escapedValue = addslashes($value); + + return "'{$escapedValue}'"; + } + + if (is_bool($value)) { + return $value ? 'true' : 'false'; + } + + if (is_int($value) || is_float($value)) { + return (string) $value; + } + + if (is_array($value)) { + return '[]'; + } + + if (is_null($value)) { + return 'null'; + } + + // Fallback + return "'{$value}'"; + } + + /** + * Format a value for use in assertions (alias for formatAsCode for backward compatibility) + */ + protected function formatValueForAssertion(string $value): string + { + // If value is already quoted or a keyword, return as-is + if ( + (str_starts_with($value, "'") && str_ends_with($value, "'")) || + $value === 'true' || + $value === 'false' || + is_numeric($value) + ) { + return $value; + } + + // Wrap in quotes + return "'{$value}'"; + } + + /** + * Extract type, format, and example from various input formats + * + * @return array{0: ?string, 1: ?string, 2: mixed} [type, format, example] + */ + private function extractTypeInfo(Schema|array|string|null $typeInfo): array + { + if ($typeInfo instanceof Schema) { + return [ + $typeInfo->type ?? null, + $typeInfo->format ?? null, + $typeInfo->example ?? null, + ]; + } + + if (is_array($typeInfo)) { + return [ + $typeInfo['type'] ?? null, + $typeInfo['format'] ?? null, + $typeInfo['example'] ?? null, + ]; + } + + if (is_string($typeInfo)) { + // Normalize type name (remove nullable prefix) + $normalizedType = ltrim($typeInfo, '?'); + + // Check for DateTime type hints + if (str_contains($normalizedType, 'Carbon') || str_contains($normalizedType, 'DateTime')) { + return ['string', 'date-time', null]; + } + + return [$normalizedType, null, null]; + } + + return [null, null, null]; + } + + /** + * Extract example data from OpenAPI spec for an endpoint + */ + protected function extractExampleFromSpec(Endpoint $endpoint): ?array + { + $operation = $this->findOperationByEndpoint($endpoint); + if (! $operation) { + return null; + } + + // Get the 200 response + $response = $operation->responses['200'] ?? $operation->responses[200] ?? null; + if (! $response) { + return null; + } + + // Get JSON content + $mediaType = $response->content['application/json'] ?? null; + if (! $mediaType) { + return null; + } + + // Try to find examples in this order: + // 1. MediaType-level example + if ($mediaType->example !== null) { + return $mediaType->example; + } + + // 2. MediaType-level examples array (use first one) + if ($mediaType->examples && is_array($mediaType->examples)) { + $firstExample = reset($mediaType->examples); + if ($firstExample && isset($firstExample->value)) { + return $firstExample->value; + } + } + + // 3. Schema-level example + if ($mediaType->schema && $mediaType->schema->example !== null) { + return $mediaType->schema->example; + } + + return null; + } + + /** + * Generate mock attributes based on schema properties + */ + protected function generateMockAttributes(Schema $schema): array + { + $attributes = []; + + // Extract the actual properties from the schema + $properties = $this->extractPropertiesFromSchema($schema); + + if (empty($properties)) { + return ['name' => 'Mock value']; + } + + foreach ($properties as $propName => $propSchema) { + $attributes[$propName] = $this->generateValue($propName, $propSchema); + } + + return $attributes; + } + + /** + * Generate the complete mock response body for an endpoint + */ + protected function generateMockResponseBody(Endpoint $endpoint): string + { + // Try to extract example from OpenAPI spec first + $example = $this->extractExampleFromSpec($endpoint); + + if ($example !== null) { + // Use example from spec + $mockData = $example; + } else { + // Generate fallback mock data + $mockData = $this->generateMockData($endpoint); + } + + // Format as PHP array syntax for the stub + return $this->formatArrayAsPhp($mockData); + } + + /** + * Format an array as PHP code string + */ + protected function formatArrayAsPhp(array $data, int $indent = 0): string + { + $indentStr = str_repeat(' ', $indent); + $lines = []; + + foreach ($data as $key => $value) { + $keyStr = is_string($key) ? "'$key'" : $key; + + if (is_array($value)) { + $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; + } elseif (is_string($value)) { + $escapedValue = addslashes($value); + $lines[] = $indentStr." $keyStr => '$escapedValue',"; + } elseif (is_bool($value)) { + $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; + } elseif (is_null($value)) { + $lines[] = $indentStr." $keyStr => null,"; + } else { + $lines[] = $indentStr." $keyStr => $value,"; + } + } + + if (empty($lines)) { + return '[]'; + } + + return "[\n".implode("\n", $lines)."\n$indentStr]"; + } +} diff --git a/generator/TestGenerators/Traits/TestValueGeneratorTrait.php b/generator/TestGenerators/Traits/TestValueGeneratorTrait.php deleted file mode 100644 index 81c2133..0000000 --- a/generator/TestGenerators/Traits/TestValueGeneratorTrait.php +++ /dev/null @@ -1,89 +0,0 @@ -entryId = 'mock-id-123'; - $dto->overtimeTypeId = 'mock-id-123'; - $dto->startedAt = 'test value'; - $dto->endedAt = 'test value'; + $dto->entryId = 'entry_id-123'; + $dto->overtimeTypeId = 'overtime_type_id-123'; + $dto->startedAt = '2025-01-15T10:30:00Z'; + $dto->endedAt = '2025-01-15T10:30:00Z'; // todo: add every other DTO field $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', data: $dto); @@ -31,10 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('approves') ->data->attributes->scoped(fn ($attributes) => $attributes - ->entryId->toBe('mock-id-123') - ->overtimeTypeId->toBe('mock-id-123') - ->startedAt->toBe('test value') - ->endedAt->toBe('test value') + ->entryId->toBe('entry_id-123') + ->overtimeTypeId->toBe('overtime_type_id-123') + ->startedAt->toBe('2025-01-15T10:30:00Z') + ->endedAt->toBe('2025-01-15T10:30:00Z') ); return true; diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 4d8b136..eaf2ba4 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -63,9 +63,9 @@ ]); $request = (new GetBudgetsRequest(include: 'test string')) - ->filter('customerId', 'test-id-123') - ->filter('budgetTypeId', 'test-id-123') - ->filter('isArchived', false); + ->filter('customerId', 'customer_id-123') + ->filter('budgetTypeId', 'budget_type_id-123') + ->filter('isArchived', true); $response = $this->timaticConnector->send($request); @@ -75,9 +75,9 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[customerId]', 'test-id-123'); - expect($query)->toHaveKey('filter[budgetTypeId]', 'test-id-123'); - expect($query)->toHaveKey('filter[isArchived]', false); + expect($query)->toHaveKey('filter[customerId]', 'customer_id-123'); + expect($query)->toHaveKey('filter[budgetTypeId]', 'budget_type_id-123'); + expect($query)->toHaveKey('filter[isArchived]', true); return true; }); @@ -110,10 +110,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Budget; - $dto->budgetTypeId = 'mock-id-123'; - $dto->customerId = 'mock-id-123'; + $dto->budgetTypeId = 'budget_type_id-123'; + $dto->customerId = 'customer_id-123'; $dto->showToCustomer = true; - $dto->changeId = 'mock-id-123'; + $dto->changeId = 'change_id-123'; // todo: add every other DTO field $this->timaticConnector->budget()->postBudgets($dto); @@ -125,10 +125,10 @@ // POST calls dont have an ID field ->data->type->toBe('budgets') ->data->attributes->scoped(fn ($attributes) => $attributes - ->budgetTypeId->toBe('mock-id-123') - ->customerId->toBe('mock-id-123') + ->budgetTypeId->toBe('budget_type_id-123') + ->customerId->toBe('customer_id-123') ->showToCustomer->toBe(true) - ->changeId->toBe('mock-id-123') + ->changeId->toBe('change_id-123') ); return true; @@ -209,10 +209,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Budget; - $dto->budgetTypeId = 'mock-id-123'; - $dto->customerId = 'mock-id-123'; + $dto->budgetTypeId = 'budget_type_id-123'; + $dto->customerId = 'customer_id-123'; $dto->showToCustomer = true; - $dto->changeId = 'mock-id-123'; + $dto->changeId = 'change_id-123'; // todo: add every other DTO field $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', data: $dto); @@ -223,10 +223,10 @@ ->toHaveKey('data') ->data->type->toBe('budgets') ->data->attributes->scoped(fn ($attributes) => $attributes - ->budgetTypeId->toBe('mock-id-123') - ->customerId->toBe('mock-id-123') + ->budgetTypeId->toBe('budget_type_id-123') + ->customerId->toBe('customer_id-123') ->showToCustomer->toBe(true) - ->changeId->toBe('mock-id-123') + ->changeId->toBe('change_id-123') ); return true; diff --git a/tests/Requests/BudgetTimeSpentTotalTest.php b/tests/Requests/BudgetTimeSpentTotalTest.php index 9ec4e67..2233339 100644 --- a/tests/Requests/BudgetTimeSpentTotalTest.php +++ b/tests/Requests/BudgetTimeSpentTotalTest.php @@ -41,7 +41,7 @@ ]); $request = (new GetBudgetTimeSpentTotalsRequest) - ->filter('budgetId', 'test-id-123'); + ->filter('budgetId', 'budget_id-123'); $response = $this->timaticConnector->send($request); @@ -51,7 +51,7 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[budgetId]', 'test-id-123'); + expect($query)->toHaveKey('filter[budgetId]', 'budget_id-123'); return true; }); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index 6a06df7..60c0645 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -42,7 +42,7 @@ ]); $request = (new GetCustomersRequest) - ->filter('externalId', 'test-id-123'); + ->filter('externalId', 'external_id-123'); $response = $this->timaticConnector->send($request); @@ -52,7 +52,7 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[externalId]', 'test-id-123'); + expect($query)->toHaveKey('filter[externalId]', 'external_id-123'); return true; }); @@ -75,10 +75,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Customer; - $dto->externalId = 'mock-id-123'; - $dto->name = 'test value'; + $dto->externalId = 'external_id-123'; + $dto->name = 'test name'; $dto->hourlyRate = 'test value'; - $dto->accountManagerUserId = 'mock-id-123'; + $dto->accountManagerUserId = 'account_manager_user_id-123'; // todo: add every other DTO field $this->timaticConnector->customer()->postCustomers($dto); @@ -90,10 +90,10 @@ // POST calls dont have an ID field ->data->type->toBe('customers') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('mock-id-123') - ->name->toBe('test value') + ->externalId->toBe('external_id-123') + ->name->toBe('test name') ->hourlyRate->toBe('test value') - ->accountManagerUserId->toBe('mock-id-123') + ->accountManagerUserId->toBe('account_manager_user_id-123') ); return true; @@ -154,10 +154,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Customer; - $dto->externalId = 'mock-id-123'; - $dto->name = 'test value'; + $dto->externalId = 'external_id-123'; + $dto->name = 'test name'; $dto->hourlyRate = 'test value'; - $dto->accountManagerUserId = 'mock-id-123'; + $dto->accountManagerUserId = 'account_manager_user_id-123'; // todo: add every other DTO field $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', data: $dto); @@ -168,10 +168,10 @@ ->toHaveKey('data') ->data->type->toBe('customers') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('mock-id-123') - ->name->toBe('test value') + ->externalId->toBe('external_id-123') + ->name->toBe('test name') ->hourlyRate->toBe('test value') - ->accountManagerUserId->toBe('mock-id-123') + ->accountManagerUserId->toBe('account_manager_user_id-123') ); return true; diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index eae4718..5db9c3b 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -84,9 +84,9 @@ ]); $request = (new GetEntriesRequest(include: 'test string')) - ->filter('userId', 'test-id-123') - ->filter('budgetId', 'test-id-123') - ->filter('startedAt', '2025-01-01T10:00:00Z'); + ->filter('userId', 'user_id-123') + ->filter('budgetId', 'budget_id-123') + ->filter('startedAt', '2025-01-15T10:30:00Z'); $response = $this->timaticConnector->send($request); @@ -96,9 +96,9 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[userId]', 'test-id-123'); - expect($query)->toHaveKey('filter[budgetId]', 'test-id-123'); - expect($query)->toHaveKey('filter[startedAt]', '2025-01-01T10:00:00Z'); + expect($query)->toHaveKey('filter[userId]', 'user_id-123'); + expect($query)->toHaveKey('filter[budgetId]', 'budget_id-123'); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-15T10:30:00Z'); return true; }); @@ -142,7 +142,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Entry; - $dto->ticketId = 'mock-id-123'; + $dto->ticketId = 'ticket_id-123'; $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; @@ -157,7 +157,7 @@ // POST calls dont have an ID field ->data->type->toBe('entries') ->data->attributes->scoped(fn ($attributes) => $attributes - ->ticketId->toBe('mock-id-123') + ->ticketId->toBe('ticket_id-123') ->ticketNumber->toBe('test value') ->ticketTitle->toBe('test value') ->ticketType->toBe('test value') @@ -263,7 +263,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Entry; - $dto->ticketId = 'mock-id-123'; + $dto->ticketId = 'ticket_id-123'; $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; @@ -277,7 +277,7 @@ ->toHaveKey('data') ->data->type->toBe('entries') ->data->attributes->scoped(fn ($attributes) => $attributes - ->ticketId->toBe('mock-id-123') + ->ticketId->toBe('ticket_id-123') ->ticketNumber->toBe('test value') ->ticketTitle->toBe('test value') ->ticketType->toBe('test value') diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index d4efbb7..e80997d 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -16,10 +16,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Event; - $dto->userId = 'mock-id-123'; - $dto->budgetId = 'mock-id-123'; - $dto->ticketId = 'mock-id-123'; - $dto->sourceId = 'mock-id-123'; + $dto->userId = 'user_id-123'; + $dto->budgetId = 'budget_id-123'; + $dto->ticketId = 'ticket_id-123'; + $dto->sourceId = 'source_id-123'; // todo: add every other DTO field $this->timaticConnector->event()->postEvents($dto); @@ -31,10 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('events') ->data->attributes->scoped(fn ($attributes) => $attributes - ->userId->toBe('mock-id-123') - ->budgetId->toBe('mock-id-123') - ->ticketId->toBe('mock-id-123') - ->sourceId->toBe('mock-id-123') + ->userId->toBe('user_id-123') + ->budgetId->toBe('budget_id-123') + ->ticketId->toBe('ticket_id-123') + ->sourceId->toBe('source_id-123') ); return true; diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index a6ae76d..0b4f169 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -16,10 +16,10 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\MarkAsExported; - $dto->entryId = 'mock-id-123'; - $dto->overtimeTypeId = 'mock-id-123'; - $dto->startedAt = 'test value'; - $dto->endedAt = 'test value'; + $dto->entryId = 'entry_id-123'; + $dto->overtimeTypeId = 'overtime_type_id-123'; + $dto->startedAt = '2025-01-15T10:30:00Z'; + $dto->endedAt = '2025-01-15T10:30:00Z'; // todo: add every other DTO field $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', data: $dto); @@ -31,10 +31,10 @@ // POST calls dont have an ID field ->data->type->toBe('markAsExporteds') ->data->attributes->scoped(fn ($attributes) => $attributes - ->entryId->toBe('mock-id-123') - ->overtimeTypeId->toBe('mock-id-123') - ->startedAt->toBe('test value') - ->endedAt->toBe('test value') + ->entryId->toBe('entry_id-123') + ->overtimeTypeId->toBe('overtime_type_id-123') + ->startedAt->toBe('2025-01-15T10:30:00Z') + ->endedAt->toBe('2025-01-15T10:30:00Z') ); return true; diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index dbfb1ce..799b149 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -16,7 +16,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\MarkAsInvoiced; - $dto->ticketId = 'mock-id-123'; + $dto->ticketId = 'ticket_id-123'; $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; @@ -31,7 +31,7 @@ // POST calls dont have an ID field ->data->type->toBe('markAsInvoiceds') ->data->attributes->scoped(fn ($attributes) => $attributes - ->ticketId->toBe('mock-id-123') + ->ticketId->toBe('ticket_id-123') ->ticketNumber->toBe('test value') ->ticketTitle->toBe('test value') ->ticketType->toBe('test value') diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index 0b57208..bf98115 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -46,9 +46,9 @@ ]); $request = (new GetOvertimesRequest) - ->filter('startedAt', '2025-01-01T10:00:00Z') - ->filter('endedAt', '2025-01-01T10:00:00Z') - ->filter('isApproved', false); + ->filter('startedAt', '2025-01-15T10:30:00Z') + ->filter('endedAt', '2025-01-15T10:30:00Z') + ->filter('isApproved', true); $response = $this->timaticConnector->send($request); @@ -58,9 +58,9 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[startedAt]', '2025-01-01T10:00:00Z'); - expect($query)->toHaveKey('filter[endedAt]', '2025-01-01T10:00:00Z'); - expect($query)->toHaveKey('filter[isApproved]', false); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-15T10:30:00Z'); + expect($query)->toHaveKey('filter[endedAt]', '2025-01-15T10:30:00Z'); + expect($query)->toHaveKey('filter[isApproved]', true); return true; }); diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index 397e647..00bc2ee 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -59,8 +59,8 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Team; - $dto->externalId = 'mock-id-123'; - $dto->name = 'test value'; + $dto->externalId = 'external_id-123'; + $dto->name = 'test name'; // todo: add every other DTO field $this->timaticConnector->team()->postTeams($dto); @@ -72,8 +72,8 @@ // POST calls dont have an ID field ->data->type->toBe('teams') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('mock-id-123') - ->name->toBe('test value') + ->externalId->toBe('external_id-123') + ->name->toBe('test name') ); return true; @@ -130,8 +130,8 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Team; - $dto->externalId = 'mock-id-123'; - $dto->name = 'test value'; + $dto->externalId = 'external_id-123'; + $dto->name = 'test name'; // todo: add every other DTO field $this->timaticConnector->team()->patchTeam(teamId: 'test string', data: $dto); @@ -142,8 +142,8 @@ ->toHaveKey('data') ->data->type->toBe('teams') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('mock-id-123') - ->name->toBe('test value') + ->externalId->toBe('external_id-123') + ->name->toBe('test name') ); return true; diff --git a/tests/Requests/TimeSpentTotalTest.php b/tests/Requests/TimeSpentTotalTest.php index 490fd29..f8b82d1 100644 --- a/tests/Requests/TimeSpentTotalTest.php +++ b/tests/Requests/TimeSpentTotalTest.php @@ -43,8 +43,8 @@ ]); $request = (new GetTimeSpentTotalsRequest) - ->filter('teamId', 'test-id-123') - ->filter('userId', 'test-id-123'); + ->filter('teamId', 'team_id-123') + ->filter('userId', 'user_id-123'); $response = $this->timaticConnector->send($request); @@ -54,8 +54,8 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[teamId]', 'test-id-123'); - expect($query)->toHaveKey('filter[userId]', 'test-id-123'); + expect($query)->toHaveKey('filter[teamId]', 'team_id-123'); + expect($query)->toHaveKey('filter[userId]', 'user_id-123'); return true; }); diff --git a/tests/Requests/UserCustomerHoursAggregateTest.php b/tests/Requests/UserCustomerHoursAggregateTest.php index cd55105..acf2ef8 100644 --- a/tests/Requests/UserCustomerHoursAggregateTest.php +++ b/tests/Requests/UserCustomerHoursAggregateTest.php @@ -40,9 +40,9 @@ ]); $request = (new GetUserCustomerHoursAggregatesRequest) - ->filter('startedAt', '2025-01-01T10:00:00Z') - ->filter('endedAt', '2025-01-01T10:00:00Z') - ->filter('teamId', 'test-id-123'); + ->filter('startedAt', '2025-01-15T10:30:00Z') + ->filter('endedAt', '2025-01-15T10:30:00Z') + ->filter('teamId', 'team_id-123'); $response = $this->timaticConnector->send($request); @@ -52,9 +52,9 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[startedAt]', '2025-01-01T10:00:00Z'); - expect($query)->toHaveKey('filter[endedAt]', '2025-01-01T10:00:00Z'); - expect($query)->toHaveKey('filter[teamId]', 'test-id-123'); + expect($query)->toHaveKey('filter[startedAt]', '2025-01-15T10:30:00Z'); + expect($query)->toHaveKey('filter[endedAt]', '2025-01-15T10:30:00Z'); + expect($query)->toHaveKey('filter[teamId]', 'team_id-123'); return true; }); diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index c05d0ee..eb3b541 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -38,7 +38,7 @@ ]); $request = (new GetUsersRequest) - ->filter('externalId', 'test-id-123'); + ->filter('externalId', 'external_id-123'); $response = $this->timaticConnector->send($request); @@ -48,7 +48,7 @@ Saloon::assertSent(function (Request $request) { $query = $request->query()->all(); - expect($query)->toHaveKey('filter[externalId]', 'test-id-123'); + expect($query)->toHaveKey('filter[externalId]', 'external_id-123'); return true; }); @@ -69,7 +69,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\User; - $dto->externalId = 'mock-id-123'; + $dto->externalId = 'external_id-123'; $dto->email = 'test@example.com'; // todo: add every other DTO field @@ -82,7 +82,7 @@ // POST calls dont have an ID field ->data->type->toBe('users') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('mock-id-123') + ->externalId->toBe('external_id-123') ->email->toBe('test@example.com') ); @@ -140,7 +140,7 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\User; - $dto->externalId = 'mock-id-123'; + $dto->externalId = 'external_id-123'; $dto->email = 'test@example.com'; // todo: add every other DTO field @@ -152,7 +152,7 @@ ->toHaveKey('data') ->data->type->toBe('users') ->data->attributes->scoped(fn ($attributes) => $attributes - ->externalId->toBe('mock-id-123') + ->externalId->toBe('external_id-123') ->email->toBe('test@example.com') ); From bd6efb7e5d7b38c41ee5d669db5df62b0b93d4a6 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 00:27:36 +0100 Subject: [PATCH 43/62] reuse DtoClassName helper --- generator/JsonApiPestTestGenerator.php | 25 ++------------ generator/JsonApiRequestGenerator.php | 25 ++------------ .../CollectionRequestTestGenerator.php | 24 ++----------- .../MutationRequestTestGenerator.php | 24 ++----------- .../SingularGetRequestTestGenerator.php | 24 ++----------- .../TestGenerators/Traits/DtoHelperTrait.php | 34 +++++++++++++++++++ 6 files changed, 46 insertions(+), 110 deletions(-) create mode 100644 generator/TestGenerators/Traits/DtoHelperTrait.php diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 62b168d..877408a 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -16,9 +16,12 @@ use Timatic\SDK\Generator\TestGenerators\DeleteRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\MutationRequestTestGenerator; use Timatic\SDK\Generator\TestGenerators\SingularGetRequestTestGenerator; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; class JsonApiPestTestGenerator extends PestTestGenerator { + use DtoHelperTrait; + protected CollectionRequestTestGenerator $collectionTestGenerator; protected MutationRequestTestGenerator $mutationTestGenerator; @@ -94,28 +97,6 @@ protected function hasDtoForEndpoint(Endpoint $endpoint): bool return array_key_exists($dtoClassName, $this->generatedCode->dtoClasses); } - /** - * Get DTO class name from endpoint - */ - protected function getDtoClassName(Endpoint $endpoint): string - { - // Use collection name to determine DTO - if ($endpoint->collection) { - $resourceName = NameHelper::resourceClassName($endpoint->collection); - - // Use Laravel's Str::singular() for correct singular form - return \Illuminate\Support\Str::singular($resourceName); - } - - // Fallback: try to parse from endpoint name - $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); - // Remove method prefix (post, patch, get) - $name = preg_replace('/^(post|patch|get)/i', '', $name); - - // Use Laravel's Str::singular() for correct singular form - return \Illuminate\Support\Str::singular(NameHelper::resourceClassName($name)); - } - /** * Use our custom JSON:API test stub */ diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 77f11d1..734f30f 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -15,10 +15,13 @@ use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Hydration\Facades\Hydrator; class JsonApiRequestGenerator extends RequestGenerator { + use DtoHelperTrait; + /** * Hook: Filter out PUT requests - not supported in JSON:API */ @@ -203,26 +206,4 @@ protected function addHydrationSupport(ClassType $classType, $namespace, Endpoin $method->addBody(');'); } } - - /** - * Get DTO class name from endpoint - */ - protected function getDtoClassName(Endpoint $endpoint): string - { - // Use collection name to determine DTO - if ($endpoint->collection) { - $resourceName = NameHelper::resourceClassName($endpoint->collection); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular($resourceName); - } - - // Fallback: try to parse from endpoint name - $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); - // Remove method prefix (post, patch, get) - $name = preg_replace('/^(post|patch|get)/i', '', $name); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular(NameHelper::resourceClassName($name)); - } } diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 3b32f1e..27693b1 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -10,12 +10,14 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class CollectionRequestTestGenerator { use DtoAssertions; + use DtoHelperTrait; use SchemaExtractorTrait; use TestDataGeneratorTrait; @@ -131,28 +133,6 @@ protected function generateMockData(Endpoint $endpoint): array ]; } - /** - * Get DTO class name from endpoint - */ - protected function getDtoClassName(Endpoint $endpoint): string - { - // Use collection name to determine DTO - if ($endpoint->collection) { - $resourceName = NameHelper::resourceClassName($endpoint->collection); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular($resourceName); - } - - // Fallback: try to parse from endpoint name - $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); - // Remove method prefix (get, post, patch) - $name = preg_replace('/^(get|post|patch)/i', '', $name); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular(NameHelper::resourceClassName($name)); - } - /** * Generate the complete filter assertion block */ diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 6e8504f..17fd233 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -10,11 +10,13 @@ use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Illuminate\Support\Str; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class MutationRequestTestGenerator { + use DtoHelperTrait; use SchemaExtractorTrait; use TestDataGeneratorTrait; @@ -273,28 +275,6 @@ protected function generateAttributeValidationsFromDto(array $properties): strin return implode("\n", $lines); } - /** - * Get the DTO class name for an endpoint - */ - protected function getDtoClassName(Endpoint $endpoint): string - { - // Try to extract from endpoint collection name - if ($endpoint->collection) { - $resourceName = NameHelper::resourceClassName($endpoint->collection); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular($resourceName); - } - - // Fallback: try to parse from endpoint name - $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); - // Remove method prefix (post, patch) - $name = preg_replace('/^(post|patch)/i', '', $name); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular(NameHelper::resourceClassName($name)); - } - /** * Generate fallback body validation when schema is not available */ diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index cf36088..ac2ddaf 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -10,12 +10,14 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class SingularGetRequestTestGenerator { use DtoAssertions; + use DtoHelperTrait; use SchemaExtractorTrait; use TestDataGeneratorTrait; @@ -107,26 +109,4 @@ protected function generateMockData(Endpoint $endpoint): array ], ]; } - - /** - * Get DTO class name from endpoint - */ - protected function getDtoClassName(Endpoint $endpoint): string - { - // Use collection name to determine DTO - if ($endpoint->collection) { - $resourceName = NameHelper::resourceClassName($endpoint->collection); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular($resourceName); - } - - // Fallback: try to parse from endpoint name - $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); - // Remove method prefix (get, post, patch) - $name = preg_replace('/^(get|post|patch)/i', '', $name); - - // Use Laravel's Str::singular() for correct singular form - return Str::singular(NameHelper::resourceClassName($name)); - } } diff --git a/generator/TestGenerators/Traits/DtoHelperTrait.php b/generator/TestGenerators/Traits/DtoHelperTrait.php new file mode 100644 index 0000000..c9dca8e --- /dev/null +++ b/generator/TestGenerators/Traits/DtoHelperTrait.php @@ -0,0 +1,34 @@ +collection) { + $resourceName = NameHelper::resourceClassName($endpoint->collection); + + // Use Laravel's Str::singular() for correct singular form + return Str::singular($resourceName); + } + + // Fallback: try to parse from endpoint name + $name = $endpoint->name ?: NameHelper::pathBasedName($endpoint); + // Remove method prefix (get, post, patch) + $name = preg_replace('/^(get|post|patch)/i', '', $name); + + // Use Laravel's Str::singular() for correct singular form + return Str::singular(NameHelper::resourceClassName($name)); + } +} From 17c4aa36b01ed42aee3cf72f4672bee60e72bea0 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 00:33:53 +0100 Subject: [PATCH 44/62] cleanup --- generator/JsonApiRequestGenerator.php | 2 -- .../CollectionRequestTestGenerator.php | 3 +- .../MutationRequestTestGenerator.php | 34 +------------------ .../SingularGetRequestTestGenerator.php | 4 +-- .../Traits/SchemaExtractorTrait.php | 22 ------------ tests/Requests/ApproveTest.php | 1 - tests/Requests/BudgetTest.php | 7 ++-- tests/Requests/BudgetTimeSpentTotalTest.php | 4 +-- tests/Requests/BudgetTypeTest.php | 4 +-- tests/Requests/CorrectionTest.php | 1 - tests/Requests/CustomerTest.php | 7 ++-- tests/Requests/DailyProgressTest.php | 4 +-- tests/Requests/EntrySuggestionTest.php | 6 ++-- tests/Requests/EntryTest.php | 7 ++-- tests/Requests/EventTest.php | 1 - tests/Requests/ExportMailTest.php | 4 +-- tests/Requests/MarkAsExportedTest.php | 1 - tests/Requests/MarkAsInvoicedTest.php | 1 - tests/Requests/OvertimeTest.php | 4 +-- tests/Requests/TeamTest.php | 7 ++-- tests/Requests/TimeSpentTotalTest.php | 4 +-- .../UserCustomerHoursAggregateTest.php | 4 +-- tests/Requests/UserTest.php | 7 ++-- 23 files changed, 35 insertions(+), 104 deletions(-) diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 734f30f..f9f4a17 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -8,8 +8,6 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; use Crescat\SaloonSdkGenerator\Helpers\MethodGeneratorHelper; -use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use Illuminate\Support\Str; use Nette\PhpGenerator\ClassType; use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 27693b1..078687f 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -8,7 +8,6 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; @@ -114,7 +113,7 @@ protected function generateMockData(Endpoint $endpoint): array $attributes = $this->generateMockAttributes($schema); } - $resourceType = $this->getResourceTypeFromSchema($schema); + $resourceType = $this->getResourceTypeFromEndpoint($endpoint); // Generate 2-3 items for collections return [ diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 17fd233..a8727b0 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -4,12 +4,10 @@ namespace Timatic\SDK\Generator\TestGenerators; -use cebe\openapi\spec\Schema; use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; @@ -210,7 +208,7 @@ protected function generateBodyValidation(Endpoint $endpoint): string $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); if (empty($properties)) { - return $this->generateFallbackBodyValidation($resourceType, $endpoint); + throw new \Exception('DTO has no properties'); } $lines = []; @@ -219,11 +217,6 @@ protected function generateBodyValidation(Endpoint $endpoint): string $lines[] = ' expect($request->body()->all())'; $lines[] = " ->toHaveKey('data')"; - // POST calls don't have an ID field in the request - if ($endpoint->method->isPost()) { - $lines[] = ' // POST calls dont have an ID field'; - } - // Generate attribute validations $attributeValidations = $this->generateAttributeValidationsFromDto($properties); if ($attributeValidations) { @@ -274,29 +267,4 @@ protected function generateAttributeValidationsFromDto(array $properties): strin return implode("\n", $lines); } - - /** - * Generate fallback body validation when schema is not available - */ - protected function generateFallbackBodyValidation(string $resourceType, Endpoint $endpoint): string - { - $lines = []; - $lines[] = ' $mockClient->assertSent(function (Request $request) {'; - $lines[] = ' expect($request->body()->all())'; - $lines[] = " ->toHaveKey('data')"; - - if ($endpoint->method->isPost()) { - $lines[] = ' // POST calls dont have an ID field'; - } - - $lines[] = " ->data->type->toBe('{$resourceType}')"; - $lines[] = ' ->data->attributes->scoped(fn ($attributes) => $attributes'; - $lines[] = " ->name->toBe('test value')"; - $lines[] = ' );'; - $lines[] = ''; - $lines[] = ' return true;'; - $lines[] = ' });'; - - return implode("\n", $lines); - } } diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index ac2ddaf..2163338 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -7,8 +7,6 @@ use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; -use Crescat\SaloonSdkGenerator\Helpers\NameHelper; -use Illuminate\Support\Str; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; @@ -99,7 +97,7 @@ protected function generateMockData(Endpoint $endpoint): array $attributes = $this->generateMockAttributes($schema); } - $resourceType = $this->getResourceTypeFromSchema($schema); + $resourceType = $this->getResourceTypeFromEndpoint($endpoint); return [ 'data' => [ diff --git a/generator/TestGenerators/Traits/SchemaExtractorTrait.php b/generator/TestGenerators/Traits/SchemaExtractorTrait.php index 7a2c911..8a6188d 100644 --- a/generator/TestGenerators/Traits/SchemaExtractorTrait.php +++ b/generator/TestGenerators/Traits/SchemaExtractorTrait.php @@ -188,28 +188,6 @@ protected function resolvePropertySchemas(array $properties): array return $resolved; } - /** - * Get the resource type from a schema (e.g., "users", "entries") - */ - protected function getResourceTypeFromSchema(Schema $schema): string - { - // Try to extract from schema title - if ($schema->title) { - return NameHelper::safeVariableName($schema->title); - } - - // Try to get from properties.type.example (JSON:API type field) - if (isset($schema->properties['type'])) { - $typeProperty = $schema->properties['type']; - if ($typeProperty instanceof Schema && $typeProperty->example) { - return $typeProperty->example; - } - } - - // Fallback to generic name - return 'resources'; - } - /** * Get the resource type for JSON:API from endpoint (camelCase plural) * API uses ->camel()->plural()->toString() for type determination diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index d4a6023..eebb77f 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -28,7 +28,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('approves') ->data->attributes->scoped(fn ($attributes) => $attributes ->entryId->toBe('entry_id-123') diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index eaf2ba4..2108bb7 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -19,7 +19,7 @@ GetBudgetsRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'budgets', 'id' => 'mock-id-1', 'attributes' => [ 'budgetTypeId' => 'mock-id-123', @@ -39,7 +39,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'budgets', 'id' => 'mock-id-2', 'attributes' => [ 'budgetTypeId' => 'mock-id-123', @@ -122,7 +122,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('budgets') ->data->attributes->scoped(fn ($attributes) => $attributes ->budgetTypeId->toBe('budget_type_id-123') @@ -139,7 +138,7 @@ Saloon::fake([ GetBudgetRequest::class => MockResponse::make([ 'data' => [ - 'type' => 'resources', + 'type' => 'budgets', 'id' => 'mock-id-123', 'attributes' => [ 'budgetTypeId' => 'mock-id-123', diff --git a/tests/Requests/BudgetTimeSpentTotalTest.php b/tests/Requests/BudgetTimeSpentTotalTest.php index 2233339..86ec251 100644 --- a/tests/Requests/BudgetTimeSpentTotalTest.php +++ b/tests/Requests/BudgetTimeSpentTotalTest.php @@ -15,7 +15,7 @@ GetBudgetTimeSpentTotalsRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'budgetTimeSpentTotals', 'id' => 'mock-id-1', 'attributes' => [ 'start' => '2025-11-22T10:40:04.065Z', @@ -26,7 +26,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'budgetTimeSpentTotals', 'id' => 'mock-id-2', 'attributes' => [ 'start' => '2025-11-22T10:40:04.065Z', diff --git a/tests/Requests/BudgetTypeTest.php b/tests/Requests/BudgetTypeTest.php index 17a6991..f8a836a 100644 --- a/tests/Requests/BudgetTypeTest.php +++ b/tests/Requests/BudgetTypeTest.php @@ -13,7 +13,7 @@ GetBudgetTypesRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'budgetTypes', 'id' => 'mock-id-1', 'attributes' => [ 'title' => 'Mock value', @@ -28,7 +28,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'budgetTypes', 'id' => 'mock-id-2', 'attributes' => [ 'title' => 'Mock value', diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index 02f6207..5c11549 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -26,7 +26,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('corrections'); return true; diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index 60c0645..ee33986 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -18,7 +18,7 @@ GetCustomersRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'customers', 'id' => 'mock-id-1', 'attributes' => [ 'externalId' => 'mock-id-123', @@ -28,7 +28,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'customers', 'id' => 'mock-id-2', 'attributes' => [ 'externalId' => 'mock-id-123', @@ -87,7 +87,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('customers') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('external_id-123') @@ -104,7 +103,7 @@ Saloon::fake([ GetCustomerRequest::class => MockResponse::make([ 'data' => [ - 'type' => 'resources', + 'type' => 'customers', 'id' => 'mock-id-123', 'attributes' => [ 'externalId' => 'mock-id-123', diff --git a/tests/Requests/DailyProgressTest.php b/tests/Requests/DailyProgressTest.php index 57b20cc..8c66689 100644 --- a/tests/Requests/DailyProgressTest.php +++ b/tests/Requests/DailyProgressTest.php @@ -14,7 +14,7 @@ GetDailyProgressesRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'dailyProgresses', 'id' => 'mock-id-1', 'attributes' => [ 'userId' => 'mock-id-123', @@ -23,7 +23,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'dailyProgresses', 'id' => 'mock-id-2', 'attributes' => [ 'userId' => 'mock-id-123', diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index 4429642..88d8dbc 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -16,7 +16,7 @@ GetEntrySuggestionsRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'entrySuggestions', 'id' => 'mock-id-1', 'attributes' => [ 'ticketId' => 'mock-id-123', @@ -30,7 +30,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'entrySuggestions', 'id' => 'mock-id-2', 'attributes' => [ 'ticketId' => 'mock-id-123', @@ -82,7 +82,7 @@ Saloon::fake([ GetEntrySuggestionRequest::class => MockResponse::make([ 'data' => [ - 'type' => 'resources', + 'type' => 'entrySuggestions', 'id' => 'mock-id-123', 'attributes' => [ 'ticketId' => 'mock-id-123', diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 5db9c3b..40ac643 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -18,7 +18,7 @@ GetEntriesRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'entries', 'id' => 'mock-id-1', 'attributes' => [ 'ticketId' => 'mock-id-123', @@ -49,7 +49,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'entries', 'id' => 'mock-id-2', 'attributes' => [ 'ticketId' => 'mock-id-123', @@ -154,7 +154,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('entries') ->data->attributes->scoped(fn ($attributes) => $attributes ->ticketId->toBe('ticket_id-123') @@ -171,7 +170,7 @@ Saloon::fake([ GetEntryRequest::class => MockResponse::make([ 'data' => [ - 'type' => 'resources', + 'type' => 'entries', 'id' => 'mock-id-123', 'attributes' => [ 'ticketId' => 'mock-id-123', diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index e80997d..4df301f 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -28,7 +28,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('events') ->data->attributes->scoped(fn ($attributes) => $attributes ->userId->toBe('user_id-123') diff --git a/tests/Requests/ExportMailTest.php b/tests/Requests/ExportMailTest.php index 58ff867..0f0af54 100644 --- a/tests/Requests/ExportMailTest.php +++ b/tests/Requests/ExportMailTest.php @@ -13,14 +13,14 @@ GetBudgetsExportMailsRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'exportMails', 'id' => 'mock-id-1', 'attributes' => [ 'name' => 'Mock value', ], ], 1 => [ - 'type' => 'resources', + 'type' => 'exportMails', 'id' => 'mock-id-2', 'attributes' => [ 'name' => 'Mock value', diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index 0b4f169..18f56c1 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -28,7 +28,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('markAsExporteds') ->data->attributes->scoped(fn ($attributes) => $attributes ->entryId->toBe('entry_id-123') diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index 799b149..3f29927 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -28,7 +28,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('markAsInvoiceds') ->data->attributes->scoped(fn ($attributes) => $attributes ->ticketId->toBe('ticket_id-123') diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index bf98115..5458237 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -14,7 +14,7 @@ GetOvertimesRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'overtimes', 'id' => 'mock-id-1', 'attributes' => [ 'entryId' => 'mock-id-123', @@ -28,7 +28,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'overtimes', 'id' => 'mock-id-2', 'attributes' => [ 'entryId' => 'mock-id-123', diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index 00bc2ee..e79ab49 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -18,7 +18,7 @@ GetTeamsRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'teams', 'id' => 'mock-id-1', 'attributes' => [ 'externalId' => 'mock-id-123', @@ -26,7 +26,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'teams', 'id' => 'mock-id-2', 'attributes' => [ 'externalId' => 'mock-id-123', @@ -69,7 +69,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('teams') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('external_id-123') @@ -84,7 +83,7 @@ Saloon::fake([ GetTeamRequest::class => MockResponse::make([ 'data' => [ - 'type' => 'resources', + 'type' => 'teams', 'id' => 'mock-id-123', 'attributes' => [ 'externalId' => 'mock-id-123', diff --git a/tests/Requests/TimeSpentTotalTest.php b/tests/Requests/TimeSpentTotalTest.php index f8b82d1..5bffc14 100644 --- a/tests/Requests/TimeSpentTotalTest.php +++ b/tests/Requests/TimeSpentTotalTest.php @@ -15,7 +15,7 @@ GetTimeSpentTotalsRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'timeSpentTotals', 'id' => 'mock-id-1', 'attributes' => [ 'start' => '2025-11-22T10:40:04.065Z', @@ -27,7 +27,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'timeSpentTotals', 'id' => 'mock-id-2', 'attributes' => [ 'start' => '2025-11-22T10:40:04.065Z', diff --git a/tests/Requests/UserCustomerHoursAggregateTest.php b/tests/Requests/UserCustomerHoursAggregateTest.php index acf2ef8..d425bae 100644 --- a/tests/Requests/UserCustomerHoursAggregateTest.php +++ b/tests/Requests/UserCustomerHoursAggregateTest.php @@ -14,7 +14,7 @@ GetUserCustomerHoursAggregatesRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'userCustomerHoursAggregates', 'id' => 'mock-id-1', 'attributes' => [ 'customerId' => 'mock-id-123', @@ -25,7 +25,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'userCustomerHoursAggregates', 'id' => 'mock-id-2', 'attributes' => [ 'customerId' => 'mock-id-123', diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index eb3b541..97ac493 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -18,7 +18,7 @@ GetUsersRequest::class => MockResponse::make([ 'data' => [ 0 => [ - 'type' => 'resources', + 'type' => 'users', 'id' => 'mock-id-1', 'attributes' => [ 'externalId' => 'mock-id-123', @@ -26,7 +26,7 @@ ], ], 1 => [ - 'type' => 'resources', + 'type' => 'users', 'id' => 'mock-id-2', 'attributes' => [ 'externalId' => 'mock-id-123', @@ -79,7 +79,6 @@ $mockClient->assertSent(function (Request $request) { expect($request->body()->all()) ->toHaveKey('data') - // POST calls dont have an ID field ->data->type->toBe('users') ->data->attributes->scoped(fn ($attributes) => $attributes ->externalId->toBe('external_id-123') @@ -94,7 +93,7 @@ Saloon::fake([ GetUserRequest::class => MockResponse::make([ 'data' => [ - 'type' => 'resources', + 'type' => 'users', 'id' => 'mock-id-123', 'attributes' => [ 'externalId' => 'mock-id-123', From 0c6fe2561ed10449124e549bce97139b70a56087 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 00:40:28 +0100 Subject: [PATCH 45/62] remove comment --- generator/TestGenerators/MutationRequestTestGenerator.php | 1 - tests/Requests/ApproveTest.php | 1 - tests/Requests/BudgetTest.php | 2 -- tests/Requests/CorrectionTest.php | 2 -- tests/Requests/CustomerTest.php | 2 -- tests/Requests/EntryTest.php | 2 -- tests/Requests/EventTest.php | 1 - tests/Requests/MarkAsExportedTest.php | 1 - tests/Requests/MarkAsInvoicedTest.php | 1 - tests/Requests/TeamTest.php | 2 -- tests/Requests/UserTest.php | 2 -- 11 files changed, 17 deletions(-) diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index a8727b0..bb6c5a1 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -102,7 +102,6 @@ protected function generateDtoInstantiation(Endpoint $endpoint): string $lines = []; $lines[] = " \$dto = new \\Timatic\\SDK\\Dto\\{$dtoClassName};"; $lines[] = $properties; - $lines[] = ' // todo: add every other DTO field'; return implode("\n", $lines); } diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index eebb77f..0458547 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -20,7 +20,6 @@ $dto->overtimeTypeId = 'overtime_type_id-123'; $dto->startedAt = '2025-01-15T10:30:00Z'; $dto->endedAt = '2025-01-15T10:30:00Z'; - // todo: add every other DTO field $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', data: $dto); Saloon::assertSent(PostOvertimeApproveRequest::class); diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 2108bb7..0e826f8 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -114,7 +114,6 @@ $dto->customerId = 'customer_id-123'; $dto->showToCustomer = true; $dto->changeId = 'change_id-123'; - // todo: add every other DTO field $this->timaticConnector->budget()->postBudgets($dto); Saloon::assertSent(PostBudgetsRequest::class); @@ -212,7 +211,6 @@ $dto->customerId = 'customer_id-123'; $dto->showToCustomer = true; $dto->changeId = 'change_id-123'; - // todo: add every other DTO field $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', data: $dto); Saloon::assertSent(PatchBudgetRequest::class); diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index 5c11549..bd0b464 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -18,7 +18,6 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Correction; $dto->name = 'test value'; - // todo: add every other DTO field $this->timaticConnector->correction()->postCorrections($dto); Saloon::assertSent(PostCorrectionsRequest::class); @@ -40,7 +39,6 @@ // Create DTO with sample data $dto = new \Timatic\SDK\Dto\Correction; $dto->name = 'test value'; - // todo: add every other DTO field $this->timaticConnector->correction()->patchCorrection(correctionId: 'test string', data: $dto); Saloon::assertSent(PatchCorrectionRequest::class); diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index ee33986..a4f55d1 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -79,7 +79,6 @@ $dto->name = 'test name'; $dto->hourlyRate = 'test value'; $dto->accountManagerUserId = 'account_manager_user_id-123'; - // todo: add every other DTO field $this->timaticConnector->customer()->postCustomers($dto); Saloon::assertSent(PostCustomersRequest::class); @@ -157,7 +156,6 @@ $dto->name = 'test name'; $dto->hourlyRate = 'test value'; $dto->accountManagerUserId = 'account_manager_user_id-123'; - // todo: add every other DTO field $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', data: $dto); Saloon::assertSent(PatchCustomerRequest::class); diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 40ac643..a652b5d 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -146,7 +146,6 @@ $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; - // todo: add every other DTO field $this->timaticConnector->entry()->postEntries($dto); Saloon::assertSent(PostEntriesRequest::class); @@ -266,7 +265,6 @@ $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; - // todo: add every other DTO field $this->timaticConnector->entry()->patchEntry(entryId: 'test string', data: $dto); Saloon::assertSent(PatchEntryRequest::class); diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index 4df301f..3dde365 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -20,7 +20,6 @@ $dto->budgetId = 'budget_id-123'; $dto->ticketId = 'ticket_id-123'; $dto->sourceId = 'source_id-123'; - // todo: add every other DTO field $this->timaticConnector->event()->postEvents($dto); Saloon::assertSent(PostEventsRequest::class); diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index 18f56c1..10e18e2 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -20,7 +20,6 @@ $dto->overtimeTypeId = 'overtime_type_id-123'; $dto->startedAt = '2025-01-15T10:30:00Z'; $dto->endedAt = '2025-01-15T10:30:00Z'; - // todo: add every other DTO field $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', data: $dto); Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index 3f29927..b5abbe0 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -20,7 +20,6 @@ $dto->ticketNumber = 'test value'; $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; - // todo: add every other DTO field $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced(entryId: 'test string', data: $dto); Saloon::assertSent(PostEntryMarkAsInvoicedRequest::class); diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index e79ab49..ac93e62 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -61,7 +61,6 @@ $dto = new \Timatic\SDK\Dto\Team; $dto->externalId = 'external_id-123'; $dto->name = 'test name'; - // todo: add every other DTO field $this->timaticConnector->team()->postTeams($dto); Saloon::assertSent(PostTeamsRequest::class); @@ -131,7 +130,6 @@ $dto = new \Timatic\SDK\Dto\Team; $dto->externalId = 'external_id-123'; $dto->name = 'test name'; - // todo: add every other DTO field $this->timaticConnector->team()->patchTeam(teamId: 'test string', data: $dto); Saloon::assertSent(PatchTeamRequest::class); diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index 97ac493..e92a01f 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -71,7 +71,6 @@ $dto = new \Timatic\SDK\Dto\User; $dto->externalId = 'external_id-123'; $dto->email = 'test@example.com'; - // todo: add every other DTO field $this->timaticConnector->user()->postUsers($dto); Saloon::assertSent(PostUsersRequest::class); @@ -141,7 +140,6 @@ $dto = new \Timatic\SDK\Dto\User; $dto->externalId = 'external_id-123'; $dto->email = 'test@example.com'; - // todo: add every other DTO field $this->timaticConnector->user()->patchUser(userId: 'test string', data: $dto); Saloon::assertSent(PatchUserRequest::class); From 95d58357df058186c7d1c6b9e1724764a465dcc2 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 00:50:40 +0100 Subject: [PATCH 46/62] recognize more fields as dateTime fields for DTOs --- generator/JsonApiDtoGenerator.php | 31 +++++++++++++++++-- .../MutationRequestTestGenerator.php | 30 +++++++++++++++--- src/Dto/Approve.php | 19 ++++++++---- src/Dto/Entry.php | 16 +++++++--- src/Dto/EntrySuggestion.php | 7 +++-- src/Dto/Event.php | 13 +++++--- src/Dto/MarkAsExported.php | 19 ++++++++---- src/Dto/MarkAsInvoiced.php | 16 +++++++--- src/Dto/Overtime.php | 19 ++++++++---- tests/Requests/ApproveTest.php | 8 ++--- tests/Requests/EntryTest.php | 31 ++++++++++--------- tests/Requests/MarkAsExportedTest.php | 8 ++--- tests/Requests/OvertimeTest.php | 25 ++++++++------- 13 files changed, 166 insertions(+), 76 deletions(-) diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php index 7b57ece..5cb73df 100644 --- a/generator/JsonApiDtoGenerator.php +++ b/generator/JsonApiDtoGenerator.php @@ -105,8 +105,11 @@ protected function addPropertyToClass( // Add #[Property] attribute $property->addAttribute(Property::class); - // Check if this is a datetime field and add #[DateTime] attribute - if ($propertySpec instanceof Schema && $propertySpec->format === 'date-time') { + // Check if this is a datetime field by format OR by naming pattern + $isDateTime = ($propertySpec instanceof Schema && $propertySpec->format === 'date-time') + || $this->looksLikeDateTimeField($propertyName); + + if ($isDateTime) { $property->addAttribute(DateTime::class); $namespace->addUse(DateTime::class); @@ -122,6 +125,30 @@ protected function addPropertyToClass( } } + protected function looksLikeDateTimeField(string $name): bool + { + $patterns = [ + '_at$', // snake_case: created_at, updated_at, started_at, ended_at, etc. + 'At$', // camelCase: createdAt, updatedAt, startedAt, endedAt, etc. + '_date$', // snake_case: birth_date, start_date, etc. + 'Date$', // camelCase: birthDate, startDate, etc. + '^date_', // snake_case: date_created, date_modified, etc. + '^date[A-Z]', // camelCase: dateCreated, dateModified, etc. + '_time$', // snake_case: start_time, end_time, etc. + 'Time$', // camelCase: startTime, endTime, etc. + '^time_', // snake_case: time_started, time_ended, etc. + '^time[A-Z]', // camelCase: timeStarted, timeEnded, etc. + ]; + + foreach ($patterns as $pattern) { + if (preg_match("/{$pattern}/", $name)) { + return true; + } + } + + return false; + } + protected function convertOpenApiTypeToPhp(Schema|Reference $schema): string { if ($schema instanceof Reference) { diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index bb6c5a1..9b814d0 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -134,8 +134,18 @@ protected function generateDtoProperties(Endpoint $endpoint): string continue; } - $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); - $lines[] = " \$dto->{$propName} = {$value};"; + // Check if this is a Carbon/DateTime field + $isDateTime = $propInfo['type'] && str_contains($propInfo['type'], 'Carbon'); + + if ($isDateTime) { + // Generate Carbon::parse() for DateTime fields + $dateString = $this->generateValue($propName, $propInfo['type']); + $lines[] = " \$dto->{$propName} = \\Carbon\\Carbon::parse('{$dateString}');"; + } else { + $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); + $lines[] = " \$dto->{$propName} = {$value};"; + } + $count++; } @@ -258,9 +268,19 @@ protected function generateAttributeValidationsFromDto(array $properties): strin continue; } - $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); - $assertionValue = $this->formatValueForAssertion($value); - $lines[] = " ->{$propName}->toBe({$assertionValue})"; + // Check if this is a Carbon/DateTime field + $isDateTime = $propInfo['type'] && str_contains($propInfo['type'], 'Carbon'); + + if ($isDateTime) { + // Generate toEqual(new \Carbon\Carbon(...)) assertion for DateTime fields + $dateString = $this->generateValue($propName, $propInfo['type']); + $lines[] = " ->{$propName}->toEqual(new \\Carbon\\Carbon('{$dateString}'))"; + } else { + $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); + $assertionValue = $this->formatValueForAssertion($value); + $lines[] = " ->{$propName}->toBe({$assertionValue})"; + } + $count++; } diff --git a/src/Dto/Approve.php b/src/Dto/Approve.php index ff3432e..83fbd90 100644 --- a/src/Dto/Approve.php +++ b/src/Dto/Approve.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class Approve extends Model @@ -14,26 +15,32 @@ class Approve extends Model public ?string $overtimeTypeId; #[Property] - public ?string $startedAt; + #[DateTime] + public ?\Carbon\Carbon $startedAt; #[Property] - public ?string $endedAt; + #[DateTime] + public ?\Carbon\Carbon $endedAt; #[Property] public ?string $percentages; #[Property] - public ?string $approvedAt; + #[DateTime] + public ?\Carbon\Carbon $approvedAt; #[Property] public ?string $approvedByUserId; #[Property] - public ?string $exportedAt; + #[DateTime] + public ?\Carbon\Carbon $exportedAt; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; } diff --git a/src/Dto/Entry.php b/src/Dto/Entry.php index 5b56aba..e348fd1 100644 --- a/src/Dto/Entry.php +++ b/src/Dto/Entry.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class Entry extends Model @@ -68,22 +69,27 @@ class Entry extends Model public ?bool $isInternal; #[Property] - public ?string $startedAt; + #[DateTime] + public ?\Carbon\Carbon $startedAt; #[Property] - public ?string $endedAt; + #[DateTime] + public ?\Carbon\Carbon $endedAt; #[Property] - public ?string $invoicedAt; + #[DateTime] + public ?\Carbon\Carbon $invoicedAt; #[Property] public ?string $isInvoiced; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; #[Property] public ?bool $isBasedOnSuggestion; diff --git a/src/Dto/EntrySuggestion.php b/src/Dto/EntrySuggestion.php index 1b21f32..9dd8b90 100644 --- a/src/Dto/EntrySuggestion.php +++ b/src/Dto/EntrySuggestion.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class EntrySuggestion extends Model @@ -29,10 +30,12 @@ class EntrySuggestion extends Model public ?string $ticketType; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; #[Property] public ?string $budgetId; diff --git a/src/Dto/Event.php b/src/Dto/Event.php index 08d6111..ab52ef7 100644 --- a/src/Dto/Event.php +++ b/src/Dto/Event.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class Event extends Model @@ -38,16 +39,20 @@ class Event extends Model public ?string $eventTypeId; #[Property] - public ?string $startedAt; + #[DateTime] + public ?\Carbon\Carbon $startedAt; #[Property] - public ?string $endedAt; + #[DateTime] + public ?\Carbon\Carbon $endedAt; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; #[Property] public ?string $isInternal; diff --git a/src/Dto/MarkAsExported.php b/src/Dto/MarkAsExported.php index 3098091..c5b0681 100644 --- a/src/Dto/MarkAsExported.php +++ b/src/Dto/MarkAsExported.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class MarkAsExported extends Model @@ -14,26 +15,32 @@ class MarkAsExported extends Model public ?string $overtimeTypeId; #[Property] - public ?string $startedAt; + #[DateTime] + public ?\Carbon\Carbon $startedAt; #[Property] - public ?string $endedAt; + #[DateTime] + public ?\Carbon\Carbon $endedAt; #[Property] public ?string $percentages; #[Property] - public ?string $approvedAt; + #[DateTime] + public ?\Carbon\Carbon $approvedAt; #[Property] public ?string $approvedByUserId; #[Property] - public ?string $exportedAt; + #[DateTime] + public ?\Carbon\Carbon $exportedAt; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; } diff --git a/src/Dto/MarkAsInvoiced.php b/src/Dto/MarkAsInvoiced.php index 4ea73d9..719ac61 100644 --- a/src/Dto/MarkAsInvoiced.php +++ b/src/Dto/MarkAsInvoiced.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class MarkAsInvoiced extends Model @@ -68,22 +69,27 @@ class MarkAsInvoiced extends Model public ?bool $isInternal; #[Property] - public ?string $startedAt; + #[DateTime] + public ?\Carbon\Carbon $startedAt; #[Property] - public ?string $endedAt; + #[DateTime] + public ?\Carbon\Carbon $endedAt; #[Property] - public ?string $invoicedAt; + #[DateTime] + public ?\Carbon\Carbon $invoicedAt; #[Property] public ?string $isInvoiced; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; #[Property] public ?bool $isBasedOnSuggestion; diff --git a/src/Dto/Overtime.php b/src/Dto/Overtime.php index 6e33ba7..a544912 100644 --- a/src/Dto/Overtime.php +++ b/src/Dto/Overtime.php @@ -3,6 +3,7 @@ namespace Timatic\SDK\Dto; use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; class Overtime extends Model @@ -14,26 +15,32 @@ class Overtime extends Model public ?string $overtimeTypeId; #[Property] - public ?string $startedAt; + #[DateTime] + public ?\Carbon\Carbon $startedAt; #[Property] - public ?string $endedAt; + #[DateTime] + public ?\Carbon\Carbon $endedAt; #[Property] public ?string $percentages; #[Property] - public ?string $approvedAt; + #[DateTime] + public ?\Carbon\Carbon $approvedAt; #[Property] public ?string $approvedByUserId; #[Property] - public ?string $exportedAt; + #[DateTime] + public ?\Carbon\Carbon $exportedAt; #[Property] - public ?string $createdAt; + #[DateTime] + public ?\Carbon\Carbon $createdAt; #[Property] - public ?string $updatedAt; + #[DateTime] + public ?\Carbon\Carbon $updatedAt; } diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index 0458547..37562fd 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -18,8 +18,8 @@ $dto = new \Timatic\SDK\Dto\Approve; $dto->entryId = 'entry_id-123'; $dto->overtimeTypeId = 'overtime_type_id-123'; - $dto->startedAt = '2025-01-15T10:30:00Z'; - $dto->endedAt = '2025-01-15T10:30:00Z'; + $dto->startedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); + $dto->endedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', data: $dto); Saloon::assertSent(PostOvertimeApproveRequest::class); @@ -31,8 +31,8 @@ ->data->attributes->scoped(fn ($attributes) => $attributes ->entryId->toBe('entry_id-123') ->overtimeTypeId->toBe('overtime_type_id-123') - ->startedAt->toBe('2025-01-15T10:30:00Z') - ->endedAt->toBe('2025-01-15T10:30:00Z') + ->startedAt->toEqual(new \Carbon\Carbon('2025-01-15T10:30:00Z')) + ->endedAt->toEqual(new \Carbon\Carbon('2025-01-15T10:30:00Z')) ); return true; diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index a652b5d..75ea64c 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -1,5 +1,6 @@ 'Mock value', 'description' => 'Mock value', 'isInternal' => true, - 'startedAt' => 'Mock value', - 'endedAt' => 'Mock value', - 'invoicedAt' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', + 'invoicedAt' => '2025-11-22T10:40:04.065Z', 'isInvoiced' => 'Mock value', 'isBasedOnSuggestion' => true, ], @@ -72,9 +73,9 @@ 'entryType' => 'Mock value', 'description' => 'Mock value', 'isInternal' => true, - 'startedAt' => 'Mock value', - 'endedAt' => 'Mock value', - 'invoicedAt' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', + 'invoicedAt' => '2025-11-22T10:40:04.065Z', 'isInvoiced' => 'Mock value', 'isBasedOnSuggestion' => true, ], @@ -128,9 +129,9 @@ ->entryType->toBe('Mock value') ->description->toBe('Mock value') ->isInternal->toBe(true) - ->startedAt->toBe('Mock value') - ->endedAt->toBe('Mock value') - ->invoicedAt->toBe('Mock value') + ->startedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->endedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->invoicedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) ->isInvoiced->toBe('Mock value') ->isBasedOnSuggestion->toBe(true); }); @@ -192,9 +193,9 @@ 'entryType' => 'Mock value', 'description' => 'Mock value', 'isInternal' => true, - 'startedAt' => 'Mock value', - 'endedAt' => 'Mock value', - 'invoicedAt' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', + 'invoicedAt' => '2025-11-22T10:40:04.065Z', 'isInvoiced' => 'Mock value', 'isBasedOnSuggestion' => true, ], @@ -233,9 +234,9 @@ ->entryType->toBe('Mock value') ->description->toBe('Mock value') ->isInternal->toBe(true) - ->startedAt->toBe('Mock value') - ->endedAt->toBe('Mock value') - ->invoicedAt->toBe('Mock value') + ->startedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->endedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->invoicedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) ->isInvoiced->toBe('Mock value') ->isBasedOnSuggestion->toBe(true); }); diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index 10e18e2..a396f4c 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -18,8 +18,8 @@ $dto = new \Timatic\SDK\Dto\MarkAsExported; $dto->entryId = 'entry_id-123'; $dto->overtimeTypeId = 'overtime_type_id-123'; - $dto->startedAt = '2025-01-15T10:30:00Z'; - $dto->endedAt = '2025-01-15T10:30:00Z'; + $dto->startedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); + $dto->endedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', data: $dto); Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); @@ -31,8 +31,8 @@ ->data->attributes->scoped(fn ($attributes) => $attributes ->entryId->toBe('entry_id-123') ->overtimeTypeId->toBe('overtime_type_id-123') - ->startedAt->toBe('2025-01-15T10:30:00Z') - ->endedAt->toBe('2025-01-15T10:30:00Z') + ->startedAt->toEqual(new \Carbon\Carbon('2025-01-15T10:30:00Z')) + ->endedAt->toEqual(new \Carbon\Carbon('2025-01-15T10:30:00Z')) ); return true; diff --git a/tests/Requests/OvertimeTest.php b/tests/Requests/OvertimeTest.php index 5458237..368e45c 100644 --- a/tests/Requests/OvertimeTest.php +++ b/tests/Requests/OvertimeTest.php @@ -1,5 +1,6 @@ [ 'entryId' => 'mock-id-123', 'overtimeTypeId' => 'mock-id-123', - 'startedAt' => 'Mock value', - 'endedAt' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', 'percentages' => 'Mock value', - 'approvedAt' => 'Mock value', + 'approvedAt' => '2025-11-22T10:40:04.065Z', 'approvedByUserId' => 'mock-id-123', - 'exportedAt' => 'Mock value', + 'exportedAt' => '2025-11-22T10:40:04.065Z', ], ], 1 => [ @@ -33,12 +34,12 @@ 'attributes' => [ 'entryId' => 'mock-id-123', 'overtimeTypeId' => 'mock-id-123', - 'startedAt' => 'Mock value', - 'endedAt' => 'Mock value', + 'startedAt' => '2025-11-22T10:40:04.065Z', + 'endedAt' => '2025-11-22T10:40:04.065Z', 'percentages' => 'Mock value', - 'approvedAt' => 'Mock value', + 'approvedAt' => '2025-11-22T10:40:04.065Z', 'approvedByUserId' => 'mock-id-123', - 'exportedAt' => 'Mock value', + 'exportedAt' => '2025-11-22T10:40:04.065Z', ], ], ], @@ -72,10 +73,10 @@ expect($dtoCollection->first()) ->entryId->toBe('mock-id-123') ->overtimeTypeId->toBe('mock-id-123') - ->startedAt->toBe('Mock value') - ->endedAt->toBe('Mock value') + ->startedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) + ->endedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) ->percentages->toBe('Mock value') - ->approvedAt->toBe('Mock value') + ->approvedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')) ->approvedByUserId->toBe('mock-id-123') - ->exportedAt->toBe('Mock value'); + ->exportedAt->toEqual(new Carbon('2025-11-22T10:40:04.065Z')); }); From 51296087fec14aab9dcded973a797fed49909400 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 09:33:30 +0100 Subject: [PATCH 47/62] exceptions instead of unused fallbacks --- generator/JsonApiDtoGenerator.php | 10 ++-------- generator/JsonApiPestTestGenerator.php | 4 ++-- .../TestGenerators/CollectionRequestTestGenerator.php | 2 +- .../TestGenerators/MutationRequestTestGenerator.php | 6 +----- .../TestGenerators/SingularGetRequestTestGenerator.php | 2 +- generator/TestGenerators/Traits/DtoAssertions.php | 9 +++------ .../TestGenerators/Traits/TestDataGeneratorTrait.php | 3 ++- 7 files changed, 12 insertions(+), 24 deletions(-) diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php index 5cb73df..3e0c45c 100644 --- a/generator/JsonApiDtoGenerator.php +++ b/generator/JsonApiDtoGenerator.php @@ -34,7 +34,7 @@ public function generate(ApiSpecification $specification): PhpFile|array protected function generateModelClass(string $className, Schema $schema): PhpFile { - $modelName = NameHelper::dtoClassName($className ?: $this->config->fallbackResourceName); + $modelName = NameHelper::dtoClassName($className); $classType = new ClassType($modelName); $classFile = new PhpFile; @@ -159,11 +159,7 @@ protected function convertOpenApiTypeToPhp(Schema|Reference $schema): string return collect($schema->type)->map(fn ($type) => $this->mapType($type))->implode('|'); } - if (is_string($schema->type)) { - return $this->mapType($schema->type, $schema->format); - } - - return 'mixed'; + return $this->mapType($schema->type, $schema->format); } protected function mapType(string $type, ?string $format = null): string @@ -176,11 +172,9 @@ protected function mapType(string $type, ?string $format = null): string 'number' => match ($format) { 'float' => 'float', 'int32', 'int64' => 'int', - default => 'int|float', }, 'array' => 'array', 'null' => 'null', - default => 'mixed', }; } } diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 877408a..0d0c709 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -130,7 +130,7 @@ protected function getTestFunctionStubPath(Endpoint $endpoint): string return $this->deleteTestGenerator->getStubPath(); } - throw \Exception('Unmatched request type'); + throw new \RuntimeException('Unmatched request type'); } /** @@ -209,6 +209,6 @@ protected function replaceAdditionalStubVariables( return $this->deleteTestGenerator->replaceStubVariables($functionStub, $endpoint); } - throw new \Exception('Unmatched request type'); + throw new \RuntimeException('Unmatched request type'); } } diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index 078687f..f06d0bc 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -103,7 +103,7 @@ protected function generateMockData(Endpoint $endpoint): array $schema = $this->getResponseSchemaForEndpoint($endpoint); if (! $schema) { - throw new \Exception('Endpoint does not exist'); + throw new \RuntimeException('Endpoint does not exist'); } // Generate mock data based on DTO if available, otherwise fallback to schema diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 9b814d0..6b51f71 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -114,10 +114,6 @@ protected function generateDtoProperties(Endpoint $endpoint): string $dtoClassName = $this->getDtoClassName($endpoint); $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); - if (empty($properties)) { - return " \$dto->name = 'test value';"; - } - $lines = []; // Limit to first 4 properties for the test, skip timestamp fields @@ -217,7 +213,7 @@ protected function generateBodyValidation(Endpoint $endpoint): string $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); if (empty($properties)) { - throw new \Exception('DTO has no properties'); + throw new \RuntimeException('DTO has no properties'); } $lines = []; diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index 2163338..67babf7 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -87,7 +87,7 @@ protected function generateMockData(Endpoint $endpoint): array $schema = $this->getResponseSchemaForEndpoint($endpoint); if (! $schema) { - throw new \Exception('schema operation not found'); + throw new \RuntimeException('schema operation not found'); } // Generate mock data based on DTO if available, otherwise fallback to schema diff --git a/generator/TestGenerators/Traits/DtoAssertions.php b/generator/TestGenerators/Traits/DtoAssertions.php index d7cdeb0..a70578c 100644 --- a/generator/TestGenerators/Traits/DtoAssertions.php +++ b/generator/TestGenerators/Traits/DtoAssertions.php @@ -123,12 +123,8 @@ protected function generateMockAttributesFromDto(string $dtoClassName): array /** * Generate a mock value for a DTO property based on its type */ - protected function generateMockValueForDtoProperty(string $propertyName, ?string $typeName): mixed + protected function generateMockValueForDtoProperty(string $propertyName, string $typeName): mixed { - if (! $typeName) { - return 'Mock value'; - } - // Normalize type name (remove nullable prefix) $typeName = ltrim($typeName, '?'); @@ -169,7 +165,8 @@ protected function generateMockValueForDtoProperty(string $propertyName, ?string return 'Mock value'; } - return 'Mock value'; + // This should never be reached with the current OpenAPI spec + throw new \RuntimeException("Unexpected type '{$typeName}' for property '{$propertyName}'"); } /** diff --git a/generator/TestGenerators/Traits/TestDataGeneratorTrait.php b/generator/TestGenerators/Traits/TestDataGeneratorTrait.php index 75c153b..cbcac63 100644 --- a/generator/TestGenerators/Traits/TestDataGeneratorTrait.php +++ b/generator/TestGenerators/Traits/TestDataGeneratorTrait.php @@ -76,7 +76,8 @@ protected function generateValue(string $propertyName, Schema|array|string|null return 'test value'; } - return 'test value'; + // This should never be reached with the current OpenAPI spec + throw new \RuntimeException("Unexpected type '{$type}' for property '{$propertyName}'"); } /** From 5ac7e4d567c03e8a7a72fe7f08b79f9408ff0658 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 10:14:42 +0100 Subject: [PATCH 48/62] remove duplicated code in mutationsRequestTestGenerator --- .../MutationRequestTestGenerator.php | 124 +++++------------- 1 file changed, 35 insertions(+), 89 deletions(-) diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 6b51f71..3cd8518 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -8,12 +8,14 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class MutationRequestTestGenerator { + use DtoAssertions; use DtoHelperTrait; use SchemaExtractorTrait; use TestDataGeneratorTrait; @@ -107,22 +109,18 @@ protected function generateDtoInstantiation(Endpoint $endpoint): string } /** - * Generate DTO property assignments + * Filter properties for test generation (skip timestamps, check DateTime) + * + * @return array */ - protected function generateDtoProperties(Endpoint $endpoint): string + protected function getFilteredPropertiesForTest(Endpoint $endpoint): array { $dtoClassName = $this->getDtoClassName($endpoint); $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); - $lines = []; + $filtered = []; - // Limit to first 4 properties for the test, skip timestamp fields - $count = 0; foreach ($properties as $propInfo) { - if ($count >= 4) { - break; - } - $propName = $propInfo['name']; // Skip read-only/auto-managed fields @@ -133,7 +131,29 @@ protected function generateDtoProperties(Endpoint $endpoint): string // Check if this is a Carbon/DateTime field $isDateTime = $propInfo['type'] && str_contains($propInfo['type'], 'Carbon'); - if ($isDateTime) { + $filtered[] = [ + 'name' => $propName, + 'type' => $propInfo['type'], + 'isDateTime' => $isDateTime, + ]; + } + + return array_slice($filtered, 0, 4); + } + + /** + * Generate DTO property assignments + */ + protected function generateDtoProperties(Endpoint $endpoint): string + { + $filteredProperties = $this->getFilteredPropertiesForTest($endpoint); + + $lines = []; + + foreach ($filteredProperties as $propInfo) { + $propName = $propInfo['name']; + + if ($propInfo['isDateTime']) { // Generate Carbon::parse() for DateTime fields $dateString = $this->generateValue($propName, $propInfo['type']); $lines[] = " \$dto->{$propName} = \\Carbon\\Carbon::parse('{$dateString}');"; @@ -141,8 +161,6 @@ protected function generateDtoProperties(Endpoint $endpoint): string $value = $this->formatAsCode($this->generateValue($propName, $propInfo['type'])); $lines[] = " \$dto->{$propName} = {$value};"; } - - $count++; } // Fallback if no properties after filtering @@ -153,68 +171,14 @@ protected function generateDtoProperties(Endpoint $endpoint): string return implode("\n", $lines); } - /** - * Get DTO properties from generated code (PhpFile objects) - * - * @return array - */ - protected function getDtoPropertiesFromGeneratedCode(string $dtoClassName): array - { - // Check if DTO exists in generated code - if (! isset($this->generatedCode->dtoClasses[$dtoClassName])) { - return []; - } - - $phpFile = $this->generatedCode->dtoClasses[$dtoClassName]; - $properties = []; - - // Get the first namespace in the file - $namespace = array_values($phpFile->getNamespaces())[0] ?? null; - if (! $namespace) { - return []; - } - - // Get the first class in the namespace - $classType = array_values($namespace->getClasses())[0] ?? null; - if (! $classType) { - return []; - } - - // Extract properties from the class - foreach ($classType->getProperties() as $property) { - // Skip static properties - if ($property->isStatic()) { - continue; - } - - $type = $property->getType(); - $typeName = null; - - if ($type) { - $typeName = (string) $type; - } - - $properties[$property->getName()] = [ - 'name' => $property->getName(), - 'type' => $typeName, - ]; - } - - return $properties; - } - /** * Generate body validation code */ protected function generateBodyValidation(Endpoint $endpoint): string { $resourceType = $this->getResourceTypeFromEndpoint($endpoint); - $dtoClassName = $this->getDtoClassName($endpoint); - $properties = $this->getDtoPropertiesFromGeneratedCode($dtoClassName); - if (empty($properties)) { - throw new \RuntimeException('DTO has no properties'); - } + $attributeValidations = $this->generateAttributeValidationsFromDto($endpoint); $lines = []; @@ -223,7 +187,6 @@ protected function generateBodyValidation(Endpoint $endpoint): string $lines[] = " ->toHaveKey('data')"; // Generate attribute validations - $attributeValidations = $this->generateAttributeValidationsFromDto($properties); if ($attributeValidations) { $lines[] = " ->data->type->toBe('{$resourceType}')"; $lines[] = ' ->data->attributes->scoped(fn ($attributes) => $attributes'; @@ -243,31 +206,16 @@ protected function generateBodyValidation(Endpoint $endpoint): string /** * Generate attribute validation chain from DTO properties - * - * @param array $properties */ - protected function generateAttributeValidationsFromDto(array $properties): string + protected function generateAttributeValidationsFromDto(Endpoint $endpoint): string { + $filteredProperties = $this->getFilteredPropertiesForTest($endpoint); $lines = []; - // Limit to first 4 properties for the test, skip timestamp fields - $count = 0; - foreach ($properties as $propInfo) { - if ($count >= 4) { - break; - } - + foreach ($filteredProperties as $propInfo) { $propName = $propInfo['name']; - // Skip read-only/auto-managed fields - if (in_array($propName, ['id', 'createdAt', 'updatedAt', 'deletedAt'])) { - continue; - } - - // Check if this is a Carbon/DateTime field - $isDateTime = $propInfo['type'] && str_contains($propInfo['type'], 'Carbon'); - - if ($isDateTime) { + if ($propInfo['isDateTime']) { // Generate toEqual(new \Carbon\Carbon(...)) assertion for DateTime fields $dateString = $this->generateValue($propName, $propInfo['type']); $lines[] = " ->{$propName}->toEqual(new \\Carbon\\Carbon('{$dateString}'))"; @@ -276,8 +224,6 @@ protected function generateAttributeValidationsFromDto(array $properties): strin $assertionValue = $this->formatValueForAssertion($value); $lines[] = " ->{$propName}->toBe({$assertionValue})"; } - - $count++; } return implode("\n", $lines); From aa8daea3dc757fdf67c00ae8c17806f21f3e6e43 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 10:37:41 +0100 Subject: [PATCH 49/62] use fork with hooks --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 485dd7b..42c382a 100644 --- a/composer.json +++ b/composer.json @@ -27,12 +27,12 @@ "laravel/pint": "^1.0", "larastan/larastan": "^3.0", "orchestra/testbench": "^8.0|^9.0|^10.0", - "crescat-io/saloon-sdk-generator": "dev-master" + "crescat-io/saloon-sdk-generator": "dev-add-hooks-to-pest-generator" }, "repositories": [ { - "type": "path", - "url": "../saloon-sdk-generator" + "type": "vcs", + "url": "https://github.com/tomasvanrijsse/saloon-sdk-generator" } ], "scripts": { From bcec33be95c9dd003a6283dd33be5e78d87cd6ca Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 10:40:03 +0100 Subject: [PATCH 50/62] skip tests for Endpoint without DTO --- generator/JsonApiPestTestGenerator.php | 31 ++++++++++++++++++ tests/Requests/ExportMailTest.php | 45 -------------------------- 2 files changed, 31 insertions(+), 45 deletions(-) delete mode 100644 tests/Requests/ExportMailTest.php diff --git a/generator/JsonApiPestTestGenerator.php b/generator/JsonApiPestTestGenerator.php index 0d0c709..5292cf9 100644 --- a/generator/JsonApiPestTestGenerator.php +++ b/generator/JsonApiPestTestGenerator.php @@ -83,9 +83,40 @@ protected function shouldIncludeEndpoint(Endpoint $endpoint): bool return false; } + // Skip endpoints where DTO has no properties (will fail during mock data generation) + if (! $this->hasValidDtoProperties($endpoint)) { + return false; + } + return true; } + /** + * Check if endpoint has a DTO with valid properties for test generation + */ + protected function hasValidDtoProperties(Endpoint $endpoint): bool + { + try { + // Try to generate mock data - will throw if DTO has no properties + if ($this->collectionTestGenerator->isCollectionRequest($endpoint)) { + $this->collectionTestGenerator->generateMockData($endpoint); + } elseif ($this->singularGetTestGenerator->isSingularGetRequest($endpoint)) { + $this->singularGetTestGenerator->generateMockData($endpoint); + } + + return true; + } catch (\RuntimeException $e) { + // DTO has no properties - skip test generation + if (str_contains($e->getMessage(), 'has no properties')) { + echo " ⊘ Skipping {$endpoint->name}: ".$e->getMessage()."\n"; + + return false; + } + // Re-throw other exceptions + throw $e; + } + } + /** * Check if a DTO exists for the endpoint */ diff --git a/tests/Requests/ExportMailTest.php b/tests/Requests/ExportMailTest.php deleted file mode 100644 index 0f0af54..0000000 --- a/tests/Requests/ExportMailTest.php +++ /dev/null @@ -1,45 +0,0 @@ -timaticConnector = new Timatic\SDK\TimaticConnector; -}); - -it('calls the getBudgetsExportMails method in the ExportMail resource', function () { - Saloon::fake([ - GetBudgetsExportMailsRequest::class => MockResponse::make([ - 'data' => [ - 0 => [ - 'type' => 'exportMails', - 'id' => 'mock-id-1', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - 1 => [ - 'type' => 'exportMails', - 'id' => 'mock-id-2', - 'attributes' => [ - 'name' => 'Mock value', - ], - ], - ], - ], 200), - ]); - - $request = (new GetBudgetsExportMailsRequest); - - $response = $this->timaticConnector->send($request); - - Saloon::assertSent(GetBudgetsExportMailsRequest::class); - - expect($response->status())->toBe(200); - - $dtoCollection = $response->dto(); - - expect($dtoCollection->first()) - ->name->toBe('Mock value'); -}); From 9983a7fb6b1e337cd2cfa4f75bca8948d11df546 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 11:03:47 +0100 Subject: [PATCH 51/62] remove schema dependency --- .../CollectionRequestTestGenerator.php | 25 +- .../MutationRequestTestGenerator.php | 4 +- .../SingularGetRequestTestGenerator.php | 25 +- .../Traits/MockJsonDataTrait.php | 38 ++++ .../Traits/ResourceTypeExtractorTrait.php | 43 ++++ .../Traits/SchemaExtractorTrait.php | 215 ------------------ .../Traits/TestDataGeneratorTrait.php | 118 ---------- 7 files changed, 102 insertions(+), 366 deletions(-) create mode 100644 generator/TestGenerators/Traits/MockJsonDataTrait.php create mode 100644 generator/TestGenerators/Traits/ResourceTypeExtractorTrait.php delete mode 100644 generator/TestGenerators/Traits/SchemaExtractorTrait.php diff --git a/generator/TestGenerators/CollectionRequestTestGenerator.php b/generator/TestGenerators/CollectionRequestTestGenerator.php index f06d0bc..20f6b67 100644 --- a/generator/TestGenerators/CollectionRequestTestGenerator.php +++ b/generator/TestGenerators/CollectionRequestTestGenerator.php @@ -10,14 +10,16 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\MockJsonDataTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\ResourceTypeExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class CollectionRequestTestGenerator { use DtoAssertions; use DtoHelperTrait; - use SchemaExtractorTrait; + use MockJsonDataTrait; + use ResourceTypeExtractorTrait; use TestDataGeneratorTrait; protected ApiSpecification $specification; @@ -67,8 +69,9 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): $nonFilterParams = $this->getNonFilterQueryParameters($endpoint); $functionStub = str_replace('{{ nonFilterParams }}', $nonFilterParams, $functionStub); - // Replace fixture with inline mock data - $mockResponseBody = $this->generateMockResponseBody($endpoint); + $mockData = $this->generateMockData($endpoint); + $mockResponseBody = $this->formatArrayAsPhp($mockData); + $functionStub = preg_replace( "/MockResponse::fixture\('[^']+'\)/", "MockResponse::make($mockResponseBody, 200)", @@ -94,23 +97,15 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): /** * Generate mock data for collection response */ - protected function generateMockData(Endpoint $endpoint): array + public function generateMockData(Endpoint $endpoint): array { // Get DTO class name from endpoint $dtoClassName = $this->getDtoClassName($endpoint); - // Try to determine the schema for this endpoint - $schema = $this->getResponseSchemaForEndpoint($endpoint); - - if (! $schema) { - throw new \RuntimeException('Endpoint does not exist'); - } - - // Generate mock data based on DTO if available, otherwise fallback to schema + // Generate mock data based on DTO - must have properties $attributes = $this->generateMockAttributesFromDto($dtoClassName); if (empty($attributes) || $attributes === ['name' => 'Mock value']) { - // Fallback to schema-based generation - $attributes = $this->generateMockAttributes($schema); + throw new \RuntimeException("DTO '{$dtoClassName}' has no properties - skipping test generation"); } $resourceType = $this->getResourceTypeFromEndpoint($endpoint); diff --git a/generator/TestGenerators/MutationRequestTestGenerator.php b/generator/TestGenerators/MutationRequestTestGenerator.php index 3cd8518..6189367 100644 --- a/generator/TestGenerators/MutationRequestTestGenerator.php +++ b/generator/TestGenerators/MutationRequestTestGenerator.php @@ -10,14 +10,14 @@ use Crescat\SaloonSdkGenerator\Helpers\NameHelper; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\ResourceTypeExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class MutationRequestTestGenerator { use DtoAssertions; use DtoHelperTrait; - use SchemaExtractorTrait; + use ResourceTypeExtractorTrait; use TestDataGeneratorTrait; protected ApiSpecification $specification; diff --git a/generator/TestGenerators/SingularGetRequestTestGenerator.php b/generator/TestGenerators/SingularGetRequestTestGenerator.php index 67babf7..a0655a9 100644 --- a/generator/TestGenerators/SingularGetRequestTestGenerator.php +++ b/generator/TestGenerators/SingularGetRequestTestGenerator.php @@ -9,14 +9,16 @@ use Crescat\SaloonSdkGenerator\Data\Generator\GeneratedCode; use Timatic\SDK\Generator\TestGenerators\Traits\DtoAssertions; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; -use Timatic\SDK\Generator\TestGenerators\Traits\SchemaExtractorTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\MockJsonDataTrait; +use Timatic\SDK\Generator\TestGenerators\Traits\ResourceTypeExtractorTrait; use Timatic\SDK\Generator\TestGenerators\Traits\TestDataGeneratorTrait; class SingularGetRequestTestGenerator { use DtoAssertions; use DtoHelperTrait; - use SchemaExtractorTrait; + use MockJsonDataTrait; + use ResourceTypeExtractorTrait; use TestDataGeneratorTrait; protected ApiSpecification $specification; @@ -52,7 +54,9 @@ public function getStubPath(): string public function replaceStubVariables(string $functionStub, Endpoint $endpoint): string { // Generate mock response body - $mockResponseBody = $this->generateMockResponseBody($endpoint); + $mockData = $this->generateMockData($endpoint); + $mockResponseBody = $this->formatArrayAsPhp($mockData); + $functionStub = str_replace( '{{ mockResponse }}', "MockResponse::make($mockResponseBody, 200)", @@ -78,24 +82,13 @@ public function replaceStubVariables(string $functionStub, Endpoint $endpoint): /** * Generate mock data for singular GET response */ - protected function generateMockData(Endpoint $endpoint): array + public function generateMockData(Endpoint $endpoint): array { // Get DTO class name from endpoint $dtoClassName = $this->getDtoClassName($endpoint); - // Try to determine the schema for this endpoint - $schema = $this->getResponseSchemaForEndpoint($endpoint); - - if (! $schema) { - throw new \RuntimeException('schema operation not found'); - } - - // Generate mock data based on DTO if available, otherwise fallback to schema + // Generate mock data based on DTO - must have properties $attributes = $this->generateMockAttributesFromDto($dtoClassName); - if (empty($attributes) || $attributes === ['name' => 'Mock value']) { - // Fallback to schema-based generation - $attributes = $this->generateMockAttributes($schema); - } $resourceType = $this->getResourceTypeFromEndpoint($endpoint); diff --git a/generator/TestGenerators/Traits/MockJsonDataTrait.php b/generator/TestGenerators/Traits/MockJsonDataTrait.php new file mode 100644 index 0000000..e2e7560 --- /dev/null +++ b/generator/TestGenerators/Traits/MockJsonDataTrait.php @@ -0,0 +1,38 @@ + $value) { + $keyStr = is_string($key) ? "'$key'" : $key; + + if (is_array($value)) { + $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; + } elseif (is_string($value)) { + $escapedValue = addslashes($value); + $lines[] = $indentStr." $keyStr => '$escapedValue',"; + } elseif (is_bool($value)) { + $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; + } elseif (is_null($value)) { + $lines[] = $indentStr." $keyStr => null,"; + } else { + $lines[] = $indentStr." $keyStr => $value,"; + } + } + + if (empty($lines)) { + return '[]'; + } + + return "[\n".implode("\n", $lines)."\n$indentStr]"; + } +} diff --git a/generator/TestGenerators/Traits/ResourceTypeExtractorTrait.php b/generator/TestGenerators/Traits/ResourceTypeExtractorTrait.php new file mode 100644 index 0000000..ba83a10 --- /dev/null +++ b/generator/TestGenerators/Traits/ResourceTypeExtractorTrait.php @@ -0,0 +1,43 @@ +collection) { + $name = $endpoint->collection; + } else { + // Fallback: parse from endpoint path + $path = $endpoint->path; + // Extract first path segment (e.g., /budgets -> budgets) + preg_match('#^/([^/]+)#', $path, $matches); + + if (count($matches) >= 2) { + throw new \RuntimeException('Resource type for "'.$path.'" does not exist'); + } + + $name = $matches[1]; + } + + $camelName = NameHelper::safeVariableName($name); + + return Str::plural($camelName); + } +} diff --git a/generator/TestGenerators/Traits/SchemaExtractorTrait.php b/generator/TestGenerators/Traits/SchemaExtractorTrait.php deleted file mode 100644 index 8a6188d..0000000 --- a/generator/TestGenerators/Traits/SchemaExtractorTrait.php +++ /dev/null @@ -1,215 +0,0 @@ -findOperationByEndpoint($endpoint); - if (! $operation) { - return null; - } - - // Get the 200 response schema - $response = $operation->responses['200'] ?? $operation->responses[200] ?? null; - if (! $response) { - return null; - } - - // Get the JSON response schema - $mediaType = $response->content['application/json'] ?? null; - if (! $mediaType || ! $mediaType->schema) { - return null; - } - - $schema = $this->resolveSchema($mediaType->schema); - - // Handle array responses (collections) - unwrap to get the item schema - if ($schema && $schema->type === 'array' && $schema->items) { - $schema = $this->resolveSchema($schema->items); - } - - return $schema; - } - - /** - * Get the request body schema for an endpoint from the ApiSpecification - */ - protected function getRequestSchemaForEndpoint(Endpoint $endpoint): ?Schema - { - $operation = $this->findOperationByEndpoint($endpoint); - if (! $operation || ! $operation->requestBody) { - return null; - } - - // Get the JSON request schema - $mediaType = $operation->requestBody->content['application/json'] ?? null; - if (! $mediaType || ! $mediaType->schema) { - return null; - } - - return $this->resolveSchema($mediaType->schema); - } - - /** - * Resolve a Schema or Reference to a Schema - */ - protected function resolveSchema(Schema|Reference|null $schemaOrRef): ?Schema - { - if ($schemaOrRef === null) { - return null; - } - - // If it's already a Schema, return it - if ($schemaOrRef instanceof Schema) { - return $schemaOrRef; - } - - // If it's a Reference, resolve it - if ($schemaOrRef instanceof Reference) { - $resolved = $schemaOrRef->resolve(); - if ($resolved instanceof Schema) { - return $resolved; - } - } - - return null; - } - - /** - * Find an Operation in the ApiSpecification by matching endpoint operationId - */ - protected function findOperationByEndpoint(Endpoint $endpoint): ?Operation - { - if (! $this->specification->paths) { - return null; - } - - // Search through all paths to find matching operation - foreach ($this->specification->paths as $pathItem) { - $httpMethod = strtolower($endpoint->method->value); - - // Get the operation for this HTTP method - $operation = match ($httpMethod) { - 'get' => $pathItem->get, - 'post' => $pathItem->post, - 'patch' => $pathItem->patch, - 'delete' => $pathItem->delete, - 'put' => $pathItem->put, - default => null, - }; - - if (! $operation) { - continue; - } - - // Match by operationId - if ($operation->operationId === $endpoint->name) { - return $operation; - } - } - - return null; - } - - /** - * Extract properties from a schema (handling JSON:API structure) - * Returns an array of Schema objects keyed by property name - * - * @return array - */ - protected function extractPropertiesFromSchema(Schema $schema): array - { - if (! $schema->properties) { - return []; - } - - // For JSON:API, properties are nested in data.attributes - if (isset($schema->properties['data'])) { - $dataSchema = $this->resolveSchema($schema->properties['data']); - if ($dataSchema && isset($dataSchema->properties['attributes'])) { - $attributesSchema = $this->resolveSchema($dataSchema->properties['attributes']); - if ($attributesSchema && $attributesSchema->properties) { - return $this->resolvePropertySchemas($attributesSchema->properties); - } - } - } - - // Fallback: check if properties has attributes directly - if (isset($schema->properties['attributes'])) { - $attributesSchema = $this->resolveSchema($schema->properties['attributes']); - if ($attributesSchema && $attributesSchema->properties) { - return $this->resolvePropertySchemas($attributesSchema->properties); - } - } - - // Direct properties - filter out JSON:API reserved fields - $properties = $schema->properties; - unset($properties['id'], $properties['type'], $properties['attributes'], $properties['relationships']); - - return $this->resolvePropertySchemas($properties); - } - - /** - * Resolve all property schemas (convert References to Schemas) - * - * @param array $properties - * @return array - */ - protected function resolvePropertySchemas(array $properties): array - { - $resolved = []; - foreach ($properties as $name => $property) { - $schema = $this->resolveSchema($property); - if ($schema) { - $resolved[$name] = $schema; - } - } - - return $resolved; - } - - /** - * Get the resource type for JSON:API from endpoint (camelCase plural) - * API uses ->camel()->plural()->toString() for type determination - */ - protected function getResourceTypeFromEndpoint(Endpoint $endpoint): string - { - $name = null; - - if ($endpoint->collection) { - $name = $endpoint->collection; - } else { - // Fallback: parse from endpoint path - $path = $endpoint->path; - // Extract first path segment (e.g., /budgets -> budgets) - preg_match('#^/([^/]+)#', $path, $matches); - $name = $matches[1] ?? 'resources'; - } - - // Convert to camelCase and ensure plural using Laravel's Str helper - $camelName = NameHelper::safeVariableName($name); - - // Use Laravel's Str::plural() for correct English pluralization - return Str::plural($camelName); - } -} diff --git a/generator/TestGenerators/Traits/TestDataGeneratorTrait.php b/generator/TestGenerators/Traits/TestDataGeneratorTrait.php index cbcac63..74a420e 100644 --- a/generator/TestGenerators/Traits/TestDataGeneratorTrait.php +++ b/generator/TestGenerators/Traits/TestDataGeneratorTrait.php @@ -5,7 +5,6 @@ namespace Timatic\SDK\Generator\TestGenerators\Traits; use cebe\openapi\spec\Schema; -use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Illuminate\Support\Str; trait TestDataGeneratorTrait @@ -170,121 +169,4 @@ private function extractTypeInfo(Schema|array|string|null $typeInfo): array return [null, null, null]; } - - /** - * Extract example data from OpenAPI spec for an endpoint - */ - protected function extractExampleFromSpec(Endpoint $endpoint): ?array - { - $operation = $this->findOperationByEndpoint($endpoint); - if (! $operation) { - return null; - } - - // Get the 200 response - $response = $operation->responses['200'] ?? $operation->responses[200] ?? null; - if (! $response) { - return null; - } - - // Get JSON content - $mediaType = $response->content['application/json'] ?? null; - if (! $mediaType) { - return null; - } - - // Try to find examples in this order: - // 1. MediaType-level example - if ($mediaType->example !== null) { - return $mediaType->example; - } - - // 2. MediaType-level examples array (use first one) - if ($mediaType->examples && is_array($mediaType->examples)) { - $firstExample = reset($mediaType->examples); - if ($firstExample && isset($firstExample->value)) { - return $firstExample->value; - } - } - - // 3. Schema-level example - if ($mediaType->schema && $mediaType->schema->example !== null) { - return $mediaType->schema->example; - } - - return null; - } - - /** - * Generate mock attributes based on schema properties - */ - protected function generateMockAttributes(Schema $schema): array - { - $attributes = []; - - // Extract the actual properties from the schema - $properties = $this->extractPropertiesFromSchema($schema); - - if (empty($properties)) { - return ['name' => 'Mock value']; - } - - foreach ($properties as $propName => $propSchema) { - $attributes[$propName] = $this->generateValue($propName, $propSchema); - } - - return $attributes; - } - - /** - * Generate the complete mock response body for an endpoint - */ - protected function generateMockResponseBody(Endpoint $endpoint): string - { - // Try to extract example from OpenAPI spec first - $example = $this->extractExampleFromSpec($endpoint); - - if ($example !== null) { - // Use example from spec - $mockData = $example; - } else { - // Generate fallback mock data - $mockData = $this->generateMockData($endpoint); - } - - // Format as PHP array syntax for the stub - return $this->formatArrayAsPhp($mockData); - } - - /** - * Format an array as PHP code string - */ - protected function formatArrayAsPhp(array $data, int $indent = 0): string - { - $indentStr = str_repeat(' ', $indent); - $lines = []; - - foreach ($data as $key => $value) { - $keyStr = is_string($key) ? "'$key'" : $key; - - if (is_array($value)) { - $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; - } elseif (is_string($value)) { - $escapedValue = addslashes($value); - $lines[] = $indentStr." $keyStr => '$escapedValue',"; - } elseif (is_bool($value)) { - $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; - } elseif (is_null($value)) { - $lines[] = $indentStr." $keyStr => null,"; - } else { - $lines[] = $indentStr." $keyStr => $value,"; - } - } - - if (empty($lines)) { - return '[]'; - } - - return "[\n".implode("\n", $lines)."\n$indentStr]"; - } } From ca2f964f34b4c1567a8d8fe996c624eda9b4aec5 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 11:26:45 +0100 Subject: [PATCH 52/62] ignore sdk-generator in tests --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index aaba036..a643768 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,6 +44,7 @@ jobs: - name: Install dependencies run: | + composer remove "crescat-io/saloon-sdk-generator" --dev --no-interaction composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction From 940873a61f42157e252e9ea66d3572796c05cea1 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 11:31:12 +0100 Subject: [PATCH 53/62] ignore other tools in pest workflow --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a643768..741c3a0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -44,7 +44,7 @@ jobs: - name: Install dependencies run: | - composer remove "crescat-io/saloon-sdk-generator" --dev --no-interaction + composer remove "crescat-io/saloon-sdk-generator" "larastan/larastan" "laravel/boost" "laravel/pint" --dev --no-interaction composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction From 7a82ef9bf4a48642087c2998f95bb05b2ac16f51 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 12:33:59 +0100 Subject: [PATCH 54/62] fix correct Model param in docblock --- generator/JsonApiDtoGenerator.php | 2 +- generator/JsonApiRequestGenerator.php | 4 ++-- generator/JsonApiResourceGenerator.php | 4 ++-- src/Dto/Approve.php | 2 +- src/Dto/Budget.php | 2 +- src/Dto/BudgetTimeSpentTotal.php | 2 +- src/Dto/BudgetType.php | 2 +- src/Dto/Correction.php | 2 +- src/Dto/Customer.php | 2 +- src/Dto/DailyProgress.php | 2 +- src/Dto/Entry.php | 2 +- src/Dto/EntrySuggestion.php | 2 +- src/Dto/Event.php | 2 +- src/Dto/ExportMail.php | 2 +- src/Dto/MarkAsExported.php | 2 +- src/Dto/MarkAsInvoiced.php | 2 +- src/Dto/Overtime.php | 2 +- src/Dto/Team.php | 2 +- src/Dto/TimeSpentTotal.php | 2 +- src/Dto/User.php | 2 +- src/Dto/UserCustomerHoursAggregate.php | 2 +- src/Hydration/Hydrator.php | 1 - src/{Concerns => Hydration}/Model.php | 3 ++- src/{Concerns => Hydration}/ModelInterface.php | 2 +- src/Requests/Approve/PostOvertimeApproveRequest.php | 4 ++-- src/Requests/Budget/PatchBudgetRequest.php | 4 ++-- src/Requests/Budget/PostBudgetsRequest.php | 4 ++-- src/Requests/Correction/PatchCorrectionRequest.php | 4 ++-- src/Requests/Correction/PostCorrectionsRequest.php | 4 ++-- src/Requests/Customer/PatchCustomerRequest.php | 4 ++-- src/Requests/Customer/PostCustomersRequest.php | 4 ++-- src/Requests/Entry/PatchEntryRequest.php | 4 ++-- src/Requests/Entry/PostEntriesRequest.php | 4 ++-- src/Requests/Event/PostEventsRequest.php | 4 ++-- .../MarkAsExported/PostOvertimeMarkAsExportedRequest.php | 4 ++-- .../MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php | 4 ++-- src/Requests/Team/PatchTeamRequest.php | 4 ++-- src/Requests/Team/PostTeamsRequest.php | 4 ++-- src/Requests/User/PatchUserRequest.php | 4 ++-- src/Requests/User/PostUsersRequest.php | 4 ++-- src/Resource/Approve.php | 4 ++-- src/Resource/Budget.php | 6 +++--- src/Resource/Correction.php | 6 +++--- src/Resource/Customer.php | 6 +++--- src/Resource/Entry.php | 6 +++--- src/Resource/Event.php | 4 ++-- src/Resource/MarkAsExported.php | 4 ++-- src/Resource/MarkAsInvoiced.php | 4 ++-- src/Resource/Team.php | 6 +++--- src/Resource/User.php | 6 +++--- 50 files changed, 84 insertions(+), 84 deletions(-) rename src/{Concerns => Hydration}/Model.php (96%) rename src/{Concerns => Hydration}/ModelInterface.php (85%) diff --git a/generator/JsonApiDtoGenerator.php b/generator/JsonApiDtoGenerator.php index 3e0c45c..5057ed8 100644 --- a/generator/JsonApiDtoGenerator.php +++ b/generator/JsonApiDtoGenerator.php @@ -13,9 +13,9 @@ use Illuminate\Support\Str; use Nette\PhpGenerator\ClassType; use Nette\PhpGenerator\PhpFile; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class JsonApiDtoGenerator extends Generator { diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index f9f4a17..946aa75 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -12,9 +12,9 @@ use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Generator\TestGenerators\Traits\DtoHelperTrait; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; class JsonApiRequestGenerator extends RequestGenerator { @@ -89,7 +89,7 @@ protected function customizeConstructor($classConstructor, ClassType $classType, $namespace->addUse(Model::class); $dataParam = new Parameter( - type: 'Timatic\\SDK\\Concerns\\Model|array|null', + type: '\\Timatic\\SDK\\Hydration\\Model|array|null', nullable: true, name: 'data', description: 'Request data', diff --git a/generator/JsonApiResourceGenerator.php b/generator/JsonApiResourceGenerator.php index e2f34fd..13a1499 100644 --- a/generator/JsonApiResourceGenerator.php +++ b/generator/JsonApiResourceGenerator.php @@ -7,7 +7,7 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\ResourceGenerator; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; class JsonApiResourceGenerator extends ResourceGenerator { @@ -70,7 +70,7 @@ protected function customizeResourceMethod(\Nette\PhpGenerator\Method $method, $ $namespace->addUse(Model::class); $dataParam = new Parameter( - type: 'Timatic\\SDK\\Concerns\\Model|array|null', + type: 'Timatic\\SDK\\Hydration\\Model|array|null', nullable: true, name: 'data', description: 'Request data', diff --git a/src/Dto/Approve.php b/src/Dto/Approve.php index 83fbd90..10ba1fe 100644 --- a/src/Dto/Approve.php +++ b/src/Dto/Approve.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Approve extends Model { diff --git a/src/Dto/Budget.php b/src/Dto/Budget.php index 3c1f311..30874f6 100644 --- a/src/Dto/Budget.php +++ b/src/Dto/Budget.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Budget extends Model { diff --git a/src/Dto/BudgetTimeSpentTotal.php b/src/Dto/BudgetTimeSpentTotal.php index 3c263dd..997afe2 100644 --- a/src/Dto/BudgetTimeSpentTotal.php +++ b/src/Dto/BudgetTimeSpentTotal.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class BudgetTimeSpentTotal extends Model { diff --git a/src/Dto/BudgetType.php b/src/Dto/BudgetType.php index dee6c34..6f63d63 100644 --- a/src/Dto/BudgetType.php +++ b/src/Dto/BudgetType.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class BudgetType extends Model { diff --git a/src/Dto/Correction.php b/src/Dto/Correction.php index c43d133..ddc11ae 100644 --- a/src/Dto/Correction.php +++ b/src/Dto/Correction.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Correction extends Model { diff --git a/src/Dto/Customer.php b/src/Dto/Customer.php index db2ecae..c6cd7e8 100644 --- a/src/Dto/Customer.php +++ b/src/Dto/Customer.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Customer extends Model { diff --git a/src/Dto/DailyProgress.php b/src/Dto/DailyProgress.php index 506e2fa..991a262 100644 --- a/src/Dto/DailyProgress.php +++ b/src/Dto/DailyProgress.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class DailyProgress extends Model { diff --git a/src/Dto/Entry.php b/src/Dto/Entry.php index e348fd1..0053562 100644 --- a/src/Dto/Entry.php +++ b/src/Dto/Entry.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Entry extends Model { diff --git a/src/Dto/EntrySuggestion.php b/src/Dto/EntrySuggestion.php index 9dd8b90..a3700d8 100644 --- a/src/Dto/EntrySuggestion.php +++ b/src/Dto/EntrySuggestion.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class EntrySuggestion extends Model { diff --git a/src/Dto/Event.php b/src/Dto/Event.php index ab52ef7..a1cbe99 100644 --- a/src/Dto/Event.php +++ b/src/Dto/Event.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Event extends Model { diff --git a/src/Dto/ExportMail.php b/src/Dto/ExportMail.php index e574dd3..c96dca6 100644 --- a/src/Dto/ExportMail.php +++ b/src/Dto/ExportMail.php @@ -2,6 +2,6 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; class ExportMail extends Model {} diff --git a/src/Dto/MarkAsExported.php b/src/Dto/MarkAsExported.php index c5b0681..987da79 100644 --- a/src/Dto/MarkAsExported.php +++ b/src/Dto/MarkAsExported.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class MarkAsExported extends Model { diff --git a/src/Dto/MarkAsInvoiced.php b/src/Dto/MarkAsInvoiced.php index 719ac61..da30593 100644 --- a/src/Dto/MarkAsInvoiced.php +++ b/src/Dto/MarkAsInvoiced.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class MarkAsInvoiced extends Model { diff --git a/src/Dto/Overtime.php b/src/Dto/Overtime.php index a544912..6bf3938 100644 --- a/src/Dto/Overtime.php +++ b/src/Dto/Overtime.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Overtime extends Model { diff --git a/src/Dto/Team.php b/src/Dto/Team.php index a3c48a5..979ec3e 100644 --- a/src/Dto/Team.php +++ b/src/Dto/Team.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class Team extends Model { diff --git a/src/Dto/TimeSpentTotal.php b/src/Dto/TimeSpentTotal.php index 23bca7a..370f7dd 100644 --- a/src/Dto/TimeSpentTotal.php +++ b/src/Dto/TimeSpentTotal.php @@ -2,9 +2,9 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class TimeSpentTotal extends Model { diff --git a/src/Dto/User.php b/src/Dto/User.php index 31a9b6b..c13609b 100644 --- a/src/Dto/User.php +++ b/src/Dto/User.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class User extends Model { diff --git a/src/Dto/UserCustomerHoursAggregate.php b/src/Dto/UserCustomerHoursAggregate.php index c87309f..2c1db76 100644 --- a/src/Dto/UserCustomerHoursAggregate.php +++ b/src/Dto/UserCustomerHoursAggregate.php @@ -2,8 +2,8 @@ namespace Timatic\SDK\Dto; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\Property; +use Timatic\SDK\Hydration\Model; class UserCustomerHoursAggregate extends Model { diff --git a/src/Hydration/Hydrator.php b/src/Hydration/Hydrator.php index 68dc4e7..35e33d1 100644 --- a/src/Hydration/Hydrator.php +++ b/src/Hydration/Hydrator.php @@ -11,7 +11,6 @@ use ReflectionException; use ReflectionNamedType; use ReflectionProperty; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Hydration\Attributes\DateTime; use Timatic\SDK\Hydration\Attributes\Property; use Timatic\SDK\Hydration\Attributes\Relationship; diff --git a/src/Concerns/Model.php b/src/Hydration/Model.php similarity index 96% rename from src/Concerns/Model.php rename to src/Hydration/Model.php index c283056..0403d79 100644 --- a/src/Concerns/Model.php +++ b/src/Hydration/Model.php @@ -2,10 +2,11 @@ declare(strict_types=1); -namespace Timatic\SDK\Concerns; +namespace Timatic\SDK\Hydration; use Illuminate\Support\Str; use ReflectionClass; +use Timatic\SDK\Concerns\HasAttributes; use Timatic\SDK\Hydration\Attributes\Property; abstract class Model implements ModelInterface diff --git a/src/Concerns/ModelInterface.php b/src/Hydration/ModelInterface.php similarity index 85% rename from src/Concerns/ModelInterface.php rename to src/Hydration/ModelInterface.php index 112c038..a57f2aa 100644 --- a/src/Concerns/ModelInterface.php +++ b/src/Hydration/ModelInterface.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Timatic\SDK\Concerns; +namespace Timatic\SDK\Hydration; interface ModelInterface { diff --git a/src/Requests/Approve/PostOvertimeApproveRequest.php b/src/Requests/Approve/PostOvertimeApproveRequest.php index 07d6b42..5d6457f 100644 --- a/src/Requests/Approve/PostOvertimeApproveRequest.php +++ b/src/Requests/Approve/PostOvertimeApproveRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Approve; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postOvertimeApprove @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $overtimeId, diff --git a/src/Requests/Budget/PatchBudgetRequest.php b/src/Requests/Budget/PatchBudgetRequest.php index e20f8dd..f82297f 100644 --- a/src/Requests/Budget/PatchBudgetRequest.php +++ b/src/Requests/Budget/PatchBudgetRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Budget; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * patchBudget @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $budgetId, diff --git a/src/Requests/Budget/PostBudgetsRequest.php b/src/Requests/Budget/PostBudgetsRequest.php index b1b40eb..c030729 100644 --- a/src/Requests/Budget/PostBudgetsRequest.php +++ b/src/Requests/Budget/PostBudgetsRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Budget; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postBudgets @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Correction/PatchCorrectionRequest.php b/src/Requests/Correction/PatchCorrectionRequest.php index f5e535e..b96fd68 100644 --- a/src/Requests/Correction/PatchCorrectionRequest.php +++ b/src/Requests/Correction/PatchCorrectionRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Correction; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * patchCorrection @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $correctionId, diff --git a/src/Requests/Correction/PostCorrectionsRequest.php b/src/Requests/Correction/PostCorrectionsRequest.php index ef756f2..a328c9e 100644 --- a/src/Requests/Correction/PostCorrectionsRequest.php +++ b/src/Requests/Correction/PostCorrectionsRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Correction; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postCorrections @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Customer/PatchCustomerRequest.php b/src/Requests/Customer/PatchCustomerRequest.php index 256f900..0822d8c 100644 --- a/src/Requests/Customer/PatchCustomerRequest.php +++ b/src/Requests/Customer/PatchCustomerRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Customer; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * patchCustomer @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $customerId, diff --git a/src/Requests/Customer/PostCustomersRequest.php b/src/Requests/Customer/PostCustomersRequest.php index eee484d..3a5fa72 100644 --- a/src/Requests/Customer/PostCustomersRequest.php +++ b/src/Requests/Customer/PostCustomersRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Customer; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postCustomers @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Entry/PatchEntryRequest.php b/src/Requests/Entry/PatchEntryRequest.php index a6fe5e1..8a38c6d 100644 --- a/src/Requests/Entry/PatchEntryRequest.php +++ b/src/Requests/Entry/PatchEntryRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Entry; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * patchEntry @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $entryId, diff --git a/src/Requests/Entry/PostEntriesRequest.php b/src/Requests/Entry/PostEntriesRequest.php index 78c34bf..b5ff24f 100644 --- a/src/Requests/Entry/PostEntriesRequest.php +++ b/src/Requests/Entry/PostEntriesRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Entry; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postEntries @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/Event/PostEventsRequest.php b/src/Requests/Event/PostEventsRequest.php index 32c33b1..b46bbd0 100644 --- a/src/Requests/Event/PostEventsRequest.php +++ b/src/Requests/Event/PostEventsRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Event; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postEvents @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php index 50c9d07..1a01e80 100644 --- a/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php +++ b/src/Requests/MarkAsExported/PostOvertimeMarkAsExportedRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\MarkAsExported; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postOvertimeMarkAsExported @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $overtimeId, diff --git a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php index a5949ef..fafe2ff 100644 --- a/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php +++ b/src/Requests/MarkAsInvoiced/PostEntryMarkAsInvoicedRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\MarkAsInvoiced; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postEntryMarkAsInvoiced @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $entryId, diff --git a/src/Requests/Team/PatchTeamRequest.php b/src/Requests/Team/PatchTeamRequest.php index fab990e..51842fe 100644 --- a/src/Requests/Team/PatchTeamRequest.php +++ b/src/Requests/Team/PatchTeamRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Team; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * patchTeam @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $teamId, diff --git a/src/Requests/Team/PostTeamsRequest.php b/src/Requests/Team/PostTeamsRequest.php index 5e2651c..f031ae8 100644 --- a/src/Requests/Team/PostTeamsRequest.php +++ b/src/Requests/Team/PostTeamsRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\Team; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postTeams @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Requests/User/PatchUserRequest.php b/src/Requests/User/PatchUserRequest.php index a8124e3..6506298 100644 --- a/src/Requests/User/PatchUserRequest.php +++ b/src/Requests/User/PatchUserRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\User; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * patchUser @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected string $userId, diff --git a/src/Requests/User/PostUsersRequest.php b/src/Requests/User/PostUsersRequest.php index bdc246e..ae43f6b 100644 --- a/src/Requests/User/PostUsersRequest.php +++ b/src/Requests/User/PostUsersRequest.php @@ -7,9 +7,9 @@ use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Timatic\SDK\Concerns\Model; use Timatic\SDK\Dto\User; use Timatic\SDK\Hydration\Facades\Hydrator; +use Timatic\SDK\Hydration\Model; /** * postUsers @@ -37,7 +37,7 @@ public function resolveEndpoint(): string } /** - * @param null|Timatic\SDK\Concerns\Model|array|null $data Request data + * @param null|\Timatic\SDK\Hydration\Model|array|null $data Request data */ public function __construct( protected Model|array|null $data = null, diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php index ac36a9d..2a5e440 100644 --- a/src/Resource/Approve.php +++ b/src/Resource/Approve.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Approve\PostOvertimeApproveRequest; class Approve extends BaseResource { /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postOvertimeApprove(string $overtimeId, Model|array|null $data = null): Response { diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index dae659b..f42eaf4 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Budget\DeleteBudgetRequest; use Timatic\SDK\Requests\Budget\GetBudgetRequest; use Timatic\SDK\Requests\Budget\GetBudgetsRequest; @@ -39,7 +39,7 @@ public function getBudgets( } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postBudgets(Model|array|null $data = null): Response { @@ -57,7 +57,7 @@ public function deleteBudget(string $budgetId): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function patchBudget(string $budgetId, Model|array|null $data = null): Response { diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index 0dc3595..7f5e449 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -4,14 +4,14 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Correction\PatchCorrectionRequest; use Timatic\SDK\Requests\Correction\PostCorrectionsRequest; class Correction extends BaseResource { /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postCorrections(Model|array|null $data = null): Response { @@ -19,7 +19,7 @@ public function postCorrections(Model|array|null $data = null): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function patchCorrection(string $correctionId, Model|array|null $data = null): Response { diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index 6734c5f..d057907 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Customer\DeleteCustomerRequest; use Timatic\SDK\Requests\Customer\GetCustomerRequest; use Timatic\SDK\Requests\Customer\GetCustomersRequest; @@ -19,7 +19,7 @@ public function getCustomers(?string $filterexternalId = null, ?string $filterex } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postCustomers(Model|array|null $data = null): Response { @@ -37,7 +37,7 @@ public function deleteCustomer(string $customerId): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function patchCustomer(string $customerId, Model|array|null $data = null): Response { diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 3bcc347..6e12c3a 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Entry\DeleteEntryRequest; use Timatic\SDK\Requests\Entry\GetEntriesRequest; use Timatic\SDK\Requests\Entry\GetEntryRequest; @@ -87,7 +87,7 @@ public function getEntries( } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postEntries(Model|array|null $data = null): Response { @@ -105,7 +105,7 @@ public function deleteEntry(string $entryId): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function patchEntry(string $entryId, Model|array|null $data = null): Response { diff --git a/src/Resource/Event.php b/src/Resource/Event.php index b1ccf90..eae14e5 100644 --- a/src/Resource/Event.php +++ b/src/Resource/Event.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Event\PostEventsRequest; class Event extends BaseResource { /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postEvents(Model|array|null $data = null): Response { diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php index d120fc0..1ce6088 100644 --- a/src/Resource/MarkAsExported.php +++ b/src/Resource/MarkAsExported.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\MarkAsExported\PostOvertimeMarkAsExportedRequest; class MarkAsExported extends BaseResource { /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postOvertimeMarkAsExported(string $overtimeId, Model|array|null $data = null): Response { diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php index aa1986b..30d98ff 100644 --- a/src/Resource/MarkAsInvoiced.php +++ b/src/Resource/MarkAsInvoiced.php @@ -4,13 +4,13 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\MarkAsInvoiced\PostEntryMarkAsInvoicedRequest; class MarkAsInvoiced extends BaseResource { /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postEntryMarkAsInvoiced(string $entryId, Model|array|null $data = null): Response { diff --git a/src/Resource/Team.php b/src/Resource/Team.php index c9b7541..bc50715 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\Team\DeleteTeamRequest; use Timatic\SDK\Requests\Team\GetTeamRequest; use Timatic\SDK\Requests\Team\GetTeamsRequest; @@ -19,7 +19,7 @@ public function getTeams(): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postTeams(Model|array|null $data = null): Response { @@ -37,7 +37,7 @@ public function deleteTeam(string $teamId): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function patchTeam(string $teamId, Model|array|null $data = null): Response { diff --git a/src/Resource/User.php b/src/Resource/User.php index b1bcfa2..ea54af9 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -4,7 +4,7 @@ use Saloon\Http\BaseResource; use Saloon\Http\Response; -use Timatic\SDK\Concerns\Model; +use Timatic\SDK\Hydration\Model; use Timatic\SDK\Requests\User\DeleteUserRequest; use Timatic\SDK\Requests\User\GetUserRequest; use Timatic\SDK\Requests\User\GetUsersRequest; @@ -19,7 +19,7 @@ public function getUsers(?string $filterexternalId = null, ?string $filterextern } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function postUsers(Model|array|null $data = null): Response { @@ -37,7 +37,7 @@ public function deleteUser(string $userId): Response } /** - * @param Timatic\SDK\Concerns\Model|array|null $data Request data + * @param Timatic\SDK\Hydration\Model|array|null $data Request data */ public function patchUser(string $userId, Model|array|null $data = null): Response { From 36becc85ffc421dd376b8f32b645010b6114703c Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 12:34:31 +0100 Subject: [PATCH 55/62] require php 8.2 --- .github/workflows/tests.yml | 2 +- README.md | 2 +- composer.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 741c3a0..8c872f7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - php: [8.1, 8.2, 8.3, 8.4] + php: [8.2, 8.3, 8.4] laravel: [10.*, 11.*, 12.*] dependency-version: [prefer-lowest, prefer-stable] exclude: diff --git a/README.md b/README.md index 560a335..7334abb 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A Laravel package for the Timatic API, built with [Saloon](https://docs.saloon.d ## Requirements -- PHP 8.1 or higher +- PHP 8.2 or higher - Laravel 10.x or higher ## Installation diff --git a/composer.json b/composer.json index 42c382a..355847f 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ } }, "require": { - "php": "^8.1", + "php": "^8.2", "saloonphp/saloon": "^3.0", "nesbot/carbon": "^2.0|^3.0", "saloonphp/pagination-plugin": "^2.0" From 20b7827ee7d66a272774f7bb44dc9362355b65e1 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 14:01:24 +0100 Subject: [PATCH 56/62] remove hydration from requests without valid DTO --- generator/JsonApiRequestGenerator.php | 19 +++++++++++++++++++ src/Requests/Change/GetChangeRequest.php | 14 -------------- src/Requests/Change/GetChangesRequest.php | 14 -------------- .../GetBudgetEntriesExportRequest.php | 14 -------------- src/Requests/Incident/GetIncidentRequest.php | 14 -------------- src/Requests/Incident/GetIncidentsRequest.php | 14 -------------- src/Requests/Me/GetMesRequest.php | 14 -------------- .../Number/GetIncidentsNumberRequest.php | 14 -------------- .../Period/GetBudgetPeriodsRequest.php | 14 -------------- 9 files changed, 19 insertions(+), 112 deletions(-) diff --git a/generator/JsonApiRequestGenerator.php b/generator/JsonApiRequestGenerator.php index 946aa75..67898a3 100644 --- a/generator/JsonApiRequestGenerator.php +++ b/generator/JsonApiRequestGenerator.php @@ -4,11 +4,13 @@ namespace Timatic\SDK\Generator; +use Crescat\SaloonSdkGenerator\Data\Generator\ApiSpecification; use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\RequestGenerator; use Crescat\SaloonSdkGenerator\Helpers\MethodGeneratorHelper; use Nette\PhpGenerator\ClassType; +use Nette\PhpGenerator\PhpFile; use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; use Timatic\SDK\Concerns\HasFilters; @@ -20,6 +22,15 @@ class JsonApiRequestGenerator extends RequestGenerator { use DtoHelperTrait; + private ApiSpecification $specification; + + public function generate(ApiSpecification $specification): PhpFile|array + { + $this->specification = $specification; + + return parent::generate($specification); + } + /** * Hook: Filter out PUT requests - not supported in JSON:API */ @@ -156,6 +167,14 @@ protected function hasFilterParameters(Endpoint $endpoint): bool */ protected function shouldHaveHydration(Endpoint $endpoint): bool { + $schemas = array_keys($this->specification->components->schemas); + $dtoName = $this->getDtoClassName($endpoint); + + if (! in_array($dtoName, $schemas)) { + // There is no schema, so also no DTO to hydrate + return false; + } + // Add hydration to GET, POST, and PATCH requests return $endpoint->method->isGet() || $endpoint->method->isPost() diff --git a/src/Requests/Change/GetChangeRequest.php b/src/Requests/Change/GetChangeRequest.php index 085a25e..9cdb833 100644 --- a/src/Requests/Change/GetChangeRequest.php +++ b/src/Requests/Change/GetChangeRequest.php @@ -4,28 +4,14 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; -use Timatic\SDK\Dto\Change; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getChange */ class GetChangeRequest extends Request { - protected $model = Change::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrate( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return "/changes/{$this->changeId}"; diff --git a/src/Requests/Change/GetChangesRequest.php b/src/Requests/Change/GetChangesRequest.php index 2192a97..9a26355 100644 --- a/src/Requests/Change/GetChangesRequest.php +++ b/src/Requests/Change/GetChangesRequest.php @@ -4,29 +4,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; -use Timatic\SDK\Dto\Change; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getChanges */ class GetChangesRequest extends Request implements Paginatable { - protected $model = Change::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrateCollection( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return '/changes'; diff --git a/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php index 24b5a28..1cb5221 100644 --- a/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php +++ b/src/Requests/EntriesExport/GetBudgetEntriesExportRequest.php @@ -4,28 +4,14 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; -use Timatic\SDK\Dto\EntriesExport; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetEntriesExport */ class GetBudgetEntriesExportRequest extends Request { - protected $model = EntriesExport::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrate( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return "/budgets/{$this->budgetId}/entries-export"; diff --git a/src/Requests/Incident/GetIncidentRequest.php b/src/Requests/Incident/GetIncidentRequest.php index 82b1cfa..41c0265 100644 --- a/src/Requests/Incident/GetIncidentRequest.php +++ b/src/Requests/Incident/GetIncidentRequest.php @@ -4,28 +4,14 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; -use Timatic\SDK\Dto\Incident; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getIncident */ class GetIncidentRequest extends Request { - protected $model = Incident::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrate( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return "/incidents/{$this->incidentId}"; diff --git a/src/Requests/Incident/GetIncidentsRequest.php b/src/Requests/Incident/GetIncidentsRequest.php index b1d73a8..77770fa 100644 --- a/src/Requests/Incident/GetIncidentsRequest.php +++ b/src/Requests/Incident/GetIncidentsRequest.php @@ -4,29 +4,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; -use Timatic\SDK\Dto\Incident; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getIncidents */ class GetIncidentsRequest extends Request implements Paginatable { - protected $model = Incident::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrateCollection( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return '/incidents'; diff --git a/src/Requests/Me/GetMesRequest.php b/src/Requests/Me/GetMesRequest.php index 973d686..107fd45 100644 --- a/src/Requests/Me/GetMesRequest.php +++ b/src/Requests/Me/GetMesRequest.php @@ -4,29 +4,15 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; use Saloon\PaginationPlugin\Contracts\Paginatable; -use Timatic\SDK\Dto\Me; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getMes */ class GetMesRequest extends Request implements Paginatable { - protected $model = Me::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrateCollection( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return '/me'; diff --git a/src/Requests/Number/GetIncidentsNumberRequest.php b/src/Requests/Number/GetIncidentsNumberRequest.php index c3d6c23..911051b 100644 --- a/src/Requests/Number/GetIncidentsNumberRequest.php +++ b/src/Requests/Number/GetIncidentsNumberRequest.php @@ -4,28 +4,14 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; -use Timatic\SDK\Dto\Number; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getIncidentsNumber */ class GetIncidentsNumberRequest extends Request { - protected $model = Number::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrate( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return "/incidents/number/{$this->incidentId}"; diff --git a/src/Requests/Period/GetBudgetPeriodsRequest.php b/src/Requests/Period/GetBudgetPeriodsRequest.php index af6beed..ddfa825 100644 --- a/src/Requests/Period/GetBudgetPeriodsRequest.php +++ b/src/Requests/Period/GetBudgetPeriodsRequest.php @@ -4,28 +4,14 @@ use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Http\Response; -use Timatic\SDK\Dto\Period; -use Timatic\SDK\Hydration\Facades\Hydrator; /** * getBudgetPeriods */ class GetBudgetPeriodsRequest extends Request { - protected $model = Period::class; - protected Method $method = Method::GET; - public function createDtoFromResponse(Response $response): mixed - { - return Hydrator::hydrate( - $this->model, - $response->json('data'), - $response->json('included') - ); - } - public function resolveEndpoint(): string { return "/budgets/{$this->budgetId}/periods"; From 1d0dfdf106ff26cd9aa8a3fe04fede962309f6c8 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 14:41:48 +0100 Subject: [PATCH 57/62] add saloon laravel plugin --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8c872f7..1ccc0f6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -45,7 +45,7 @@ jobs: - name: Install dependencies run: | composer remove "crescat-io/saloon-sdk-generator" "larastan/larastan" "laravel/boost" "laravel/pint" --dev --no-interaction - composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" " saloonphp/laravel-plugin" --no-interaction --no-update composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: Run Pest tests From 377ced060ec41b926a8c7d289580fdc774fcea7d Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 15:22:21 +0100 Subject: [PATCH 58/62] drop lowest compatibility --- .github/workflows/tests.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1ccc0f6..cd99b2e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,15 +16,8 @@ jobs: matrix: php: [8.2, 8.3, 8.4] laravel: [10.*, 11.*, 12.*] - dependency-version: [prefer-lowest, prefer-stable] - exclude: - # Laravel 11+ requires PHP 8.2+ - - laravel: 11.* - php: 8.1 - - laravel: 12.* - php: 8.1 - - name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} (${{ matrix.dependency-version }}) + + name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} steps: - name: Checkout code From f0b6881c2a296387f0f448a59bea8a8afe30b0f6 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Sun, 23 Nov 2025 15:37:40 +0100 Subject: [PATCH 59/62] remove unused filters from resources --- generator/JsonApiResourceGenerator.php | 22 ++++++ src/Resource/Approve.php | 3 - src/Resource/Budget.php | 30 +------- src/Resource/BudgetTimeSpentTotal.php | 2 +- src/Resource/Correction.php | 6 -- src/Resource/Customer.php | 8 +-- src/Resource/EntriesExport.php | 73 +------------------ src/Resource/Entry.php | 78 +-------------------- src/Resource/EntrySuggestion.php | 12 +--- src/Resource/Event.php | 3 - src/Resource/MarkAsExported.php | 3 - src/Resource/MarkAsInvoiced.php | 3 - src/Resource/Overtime.php | 30 +------- src/Resource/Team.php | 6 -- src/Resource/TimeSpentTotal.php | 10 +-- src/Resource/User.php | 8 +-- src/Resource/UserCustomerHoursAggregate.php | 36 +--------- 17 files changed, 39 insertions(+), 294 deletions(-) diff --git a/generator/JsonApiResourceGenerator.php b/generator/JsonApiResourceGenerator.php index 13a1499..1c68f83 100644 --- a/generator/JsonApiResourceGenerator.php +++ b/generator/JsonApiResourceGenerator.php @@ -7,6 +7,8 @@ use Crescat\SaloonSdkGenerator\Data\Generator\Endpoint; use Crescat\SaloonSdkGenerator\Data\Generator\Parameter; use Crescat\SaloonSdkGenerator\Generators\ResourceGenerator; +use Crescat\SaloonSdkGenerator\Helpers\NameHelper; +use Nette\PhpGenerator\Method; use Timatic\SDK\Hydration\Model; class JsonApiResourceGenerator extends ResourceGenerator @@ -79,4 +81,24 @@ protected function customizeResourceMethod(\Nette\PhpGenerator\Method $method, $ $this->addPropertyToMethod($method, $dataParam); $args[] = new \Nette\PhpGenerator\Literal('$data'); } + + protected function addPropertyToMethod(Method $method, Parameter $parameter): Method + { + $name = NameHelper::safeVariableName($parameter->name); + + if (str_contains($parameter->name, 'filter')) { + return $method; + } + + $param = $method + ->addParameter($name) + ->setType($parameter->type) + ->setNullable($parameter->nullable); + + if ($parameter->nullable) { + $param->setDefaultValue(null); + } + + return $method; + } } diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php index 2a5e440..3b47749 100644 --- a/src/Resource/Approve.php +++ b/src/Resource/Approve.php @@ -9,9 +9,6 @@ class Approve extends BaseResource { - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postOvertimeApprove(string $overtimeId, Model|array|null $data = null): Response { return $this->connector->send(new PostOvertimeApproveRequest($overtimeId, $data)); diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php index f42eaf4..362f798 100644 --- a/src/Resource/Budget.php +++ b/src/Resource/Budget.php @@ -13,34 +13,11 @@ class Budget extends BaseResource { - public function getBudgets( - ?int $filtercustomerId = null, - ?int $filtercustomerIdeq = null, - ?int $filtercustomerIdnq = null, - ?int $filtercustomerIdgt = null, - ?int $filtercustomerIdlt = null, - ?int $filtercustomerIdgte = null, - ?int $filtercustomerIdlte = null, - ?int $filtercustomerIdcontains = null, - ?string $filterbudgetTypeId = null, - ?string $filterbudgetTypeIdeq = null, - ?string $filterbudgetTypeIdnq = null, - ?string $filterbudgetTypeIdgt = null, - ?string $filterbudgetTypeIdlt = null, - ?string $filterbudgetTypeIdgte = null, - ?string $filterbudgetTypeIdlte = null, - ?string $filterbudgetTypeIdcontains = null, - ?string $filterisArchived = null, - ?string $filtercustomerExternalId = null, - ?string $filtershowToCustomer = null, - ?string $include = null, - ): Response { + public function getBudgets(?string $include = null): Response + { return $this->connector->send(new GetBudgetsRequest($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postBudgets(Model|array|null $data = null): Response { return $this->connector->send(new PostBudgetsRequest($data)); @@ -56,9 +33,6 @@ public function deleteBudget(string $budgetId): Response return $this->connector->send(new DeleteBudgetRequest($budgetId)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function patchBudget(string $budgetId, Model|array|null $data = null): Response { return $this->connector->send(new PatchBudgetRequest($budgetId, $data)); diff --git a/src/Resource/BudgetTimeSpentTotal.php b/src/Resource/BudgetTimeSpentTotal.php index 40ff14a..130a9ba 100644 --- a/src/Resource/BudgetTimeSpentTotal.php +++ b/src/Resource/BudgetTimeSpentTotal.php @@ -8,7 +8,7 @@ class BudgetTimeSpentTotal extends BaseResource { - public function getBudgetTimeSpentTotals(?string $filterbudgetId = null, ?string $filterbudgetIdeq = null): Response + public function getBudgetTimeSpentTotals(): Response { return $this->connector->send(new GetBudgetTimeSpentTotalsRequest($filterbudgetId, $filterbudgetIdeq)); } diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php index 7f5e449..13fe514 100644 --- a/src/Resource/Correction.php +++ b/src/Resource/Correction.php @@ -10,17 +10,11 @@ class Correction extends BaseResource { - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postCorrections(Model|array|null $data = null): Response { return $this->connector->send(new PostCorrectionsRequest($data)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function patchCorrection(string $correctionId, Model|array|null $data = null): Response { return $this->connector->send(new PatchCorrectionRequest($correctionId, $data)); diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php index d057907..610ac2c 100644 --- a/src/Resource/Customer.php +++ b/src/Resource/Customer.php @@ -13,14 +13,11 @@ class Customer extends BaseResource { - public function getCustomers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response + public function getCustomers(): Response { return $this->connector->send(new GetCustomersRequest($filterexternalId, $filterexternalIdeq)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postCustomers(Model|array|null $data = null): Response { return $this->connector->send(new PostCustomersRequest($data)); @@ -36,9 +33,6 @@ public function deleteCustomer(string $customerId): Response return $this->connector->send(new DeleteCustomerRequest($customerId)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function patchCustomer(string $customerId, Model|array|null $data = null): Response { return $this->connector->send(new PatchCustomerRequest($customerId, $data)); diff --git a/src/Resource/EntriesExport.php b/src/Resource/EntriesExport.php index 637e3e7..3c55fd6 100644 --- a/src/Resource/EntriesExport.php +++ b/src/Resource/EntriesExport.php @@ -8,77 +8,8 @@ class EntriesExport extends BaseResource { - public function getBudgetEntriesExport( - string $budgetId, - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ?string $filteruserIdnq = null, - ?string $filteruserIdgt = null, - ?string $filteruserIdlt = null, - ?string $filteruserIdgte = null, - ?string $filteruserIdlte = null, - ?string $filteruserIdcontains = null, - ?string $filterbudgetId = null, - ?string $filterbudgetIdeq = null, - ?string $filterbudgetIdnq = null, - ?string $filterbudgetIdgt = null, - ?string $filterbudgetIdlt = null, - ?string $filterbudgetIdgte = null, - ?string $filterbudgetIdlte = null, - ?string $filterbudgetIdcontains = null, - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterhasOvertime = null, - ?string $filterhasOvertimeeq = null, - ?string $filterhasOvertimenq = null, - ?string $filterhasOvertimegt = null, - ?string $filterhasOvertimelt = null, - ?string $filterhasOvertimegte = null, - ?string $filterhasOvertimelte = null, - ?string $filterhasOvertimecontains = null, - ?string $filteruserFullName = null, - ?string $filteruserFullNameeq = null, - ?string $filteruserFullNamenq = null, - ?string $filteruserFullNamegt = null, - ?string $filteruserFullNamelt = null, - ?string $filteruserFullNamegte = null, - ?string $filteruserFullNamelte = null, - ?string $filteruserFullNamecontains = null, - ?string $filtercustomerId = null, - ?string $filtercustomerIdeq = null, - ?string $filtercustomerIdnq = null, - ?string $filtercustomerIdgt = null, - ?string $filtercustomerIdlt = null, - ?string $filtercustomerIdgte = null, - ?string $filtercustomerIdlte = null, - ?string $filtercustomerIdcontains = null, - ?string $filterticketNumber = null, - ?string $filterticketNumbereq = null, - ?string $filterticketNumbernq = null, - ?string $filterticketNumbergt = null, - ?string $filterticketNumberlt = null, - ?string $filterticketNumbergte = null, - ?string $filterticketNumberlte = null, - ?string $filterticketNumbercontains = null, - ?string $filtersettlement = null, - ?string $filterisInvoiced = null, - ?string $filterisInvoiceable = null, - ?string $include = null, - ): Response { + public function getBudgetEntriesExport(string $budgetId, ?string $include = null): Response + { return $this->connector->send(new GetBudgetEntriesExportRequest($budgetId, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); } } diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php index 6e12c3a..971b44b 100644 --- a/src/Resource/Entry.php +++ b/src/Resource/Entry.php @@ -13,82 +13,11 @@ class Entry extends BaseResource { - public function getEntries( - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ?string $filteruserIdnq = null, - ?string $filteruserIdgt = null, - ?string $filteruserIdlt = null, - ?string $filteruserIdgte = null, - ?string $filteruserIdlte = null, - ?string $filteruserIdcontains = null, - ?string $filterbudgetId = null, - ?string $filterbudgetIdeq = null, - ?string $filterbudgetIdnq = null, - ?string $filterbudgetIdgt = null, - ?string $filterbudgetIdlt = null, - ?string $filterbudgetIdgte = null, - ?string $filterbudgetIdlte = null, - ?string $filterbudgetIdcontains = null, - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterhasOvertime = null, - ?string $filterhasOvertimeeq = null, - ?string $filterhasOvertimenq = null, - ?string $filterhasOvertimegt = null, - ?string $filterhasOvertimelt = null, - ?string $filterhasOvertimegte = null, - ?string $filterhasOvertimelte = null, - ?string $filterhasOvertimecontains = null, - ?string $filteruserFullName = null, - ?string $filteruserFullNameeq = null, - ?string $filteruserFullNamenq = null, - ?string $filteruserFullNamegt = null, - ?string $filteruserFullNamelt = null, - ?string $filteruserFullNamegte = null, - ?string $filteruserFullNamelte = null, - ?string $filteruserFullNamecontains = null, - ?string $filtercustomerId = null, - ?string $filtercustomerIdeq = null, - ?string $filtercustomerIdnq = null, - ?string $filtercustomerIdgt = null, - ?string $filtercustomerIdlt = null, - ?string $filtercustomerIdgte = null, - ?string $filtercustomerIdlte = null, - ?string $filtercustomerIdcontains = null, - ?string $filterticketNumber = null, - ?string $filterticketNumbereq = null, - ?string $filterticketNumbernq = null, - ?string $filterticketNumbergt = null, - ?string $filterticketNumberlt = null, - ?string $filterticketNumbergte = null, - ?string $filterticketNumberlte = null, - ?string $filterticketNumbercontains = null, - ?string $filtersettlement = null, - ?string $filterisInvoiced = null, - ?string $filterisInvoiceable = null, - ?string $include = null, - ): Response { + public function getEntries(?string $include = null): Response + { return $this->connector->send(new GetEntriesRequest($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postEntries(Model|array|null $data = null): Response { return $this->connector->send(new PostEntriesRequest($data)); @@ -104,9 +33,6 @@ public function deleteEntry(string $entryId): Response return $this->connector->send(new DeleteEntryRequest($entryId)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function patchEntry(string $entryId, Model|array|null $data = null): Response { return $this->connector->send(new PatchEntryRequest($entryId, $data)); diff --git a/src/Resource/EntrySuggestion.php b/src/Resource/EntrySuggestion.php index 2ce5833..ced6e0e 100644 --- a/src/Resource/EntrySuggestion.php +++ b/src/Resource/EntrySuggestion.php @@ -10,16 +10,8 @@ class EntrySuggestion extends BaseResource { - public function getEntrySuggestions( - ?string $filterdate = null, - ?string $filterdateeq = null, - ?string $filterdatenq = null, - ?string $filterdategt = null, - ?string $filterdatelt = null, - ?string $filterdategte = null, - ?string $filterdatelte = null, - ?string $filterdatecontains = null, - ): Response { + public function getEntrySuggestions(): Response + { return $this->connector->send(new GetEntrySuggestionsRequest($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); } diff --git a/src/Resource/Event.php b/src/Resource/Event.php index eae14e5..a7a2eec 100644 --- a/src/Resource/Event.php +++ b/src/Resource/Event.php @@ -9,9 +9,6 @@ class Event extends BaseResource { - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postEvents(Model|array|null $data = null): Response { return $this->connector->send(new PostEventsRequest($data)); diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php index 1ce6088..24cf1ed 100644 --- a/src/Resource/MarkAsExported.php +++ b/src/Resource/MarkAsExported.php @@ -9,9 +9,6 @@ class MarkAsExported extends BaseResource { - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postOvertimeMarkAsExported(string $overtimeId, Model|array|null $data = null): Response { return $this->connector->send(new PostOvertimeMarkAsExportedRequest($overtimeId, $data)); diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php index 30d98ff..b0ed769 100644 --- a/src/Resource/MarkAsInvoiced.php +++ b/src/Resource/MarkAsInvoiced.php @@ -9,9 +9,6 @@ class MarkAsInvoiced extends BaseResource { - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postEntryMarkAsInvoiced(string $entryId, Model|array|null $data = null): Response { return $this->connector->send(new PostEntryMarkAsInvoicedRequest($entryId, $data)); diff --git a/src/Resource/Overtime.php b/src/Resource/Overtime.php index a8b1edb..4a9b49e 100644 --- a/src/Resource/Overtime.php +++ b/src/Resource/Overtime.php @@ -8,34 +8,8 @@ class Overtime extends BaseResource { - public function getOvertimes( - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterisApproved = null, - ?string $filterapprovedAt = null, - ?string $filterapprovedAteq = null, - ?string $filterapprovedAtnq = null, - ?string $filterapprovedAtgt = null, - ?string $filterapprovedAtlt = null, - ?string $filterapprovedAtgte = null, - ?string $filterapprovedAtlte = null, - ?string $filterapprovedAtcontains = null, - ?string $filterisExported = null, - ): Response { + public function getOvertimes(): Response + { return $this->connector->send(new GetOvertimesRequest($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); } } diff --git a/src/Resource/Team.php b/src/Resource/Team.php index bc50715..1c08479 100644 --- a/src/Resource/Team.php +++ b/src/Resource/Team.php @@ -18,9 +18,6 @@ public function getTeams(): Response return $this->connector->send(new GetTeamsRequest); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postTeams(Model|array|null $data = null): Response { return $this->connector->send(new PostTeamsRequest($data)); @@ -36,9 +33,6 @@ public function deleteTeam(string $teamId): Response return $this->connector->send(new DeleteTeamRequest($teamId)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function patchTeam(string $teamId, Model|array|null $data = null): Response { return $this->connector->send(new PatchTeamRequest($teamId, $data)); diff --git a/src/Resource/TimeSpentTotal.php b/src/Resource/TimeSpentTotal.php index c5b9fda..f82664b 100644 --- a/src/Resource/TimeSpentTotal.php +++ b/src/Resource/TimeSpentTotal.php @@ -8,14 +8,8 @@ class TimeSpentTotal extends BaseResource { - public function getTimeSpentTotals( - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterteamId = null, - ?string $filterteamIdeq = null, - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ): Response { + public function getTimeSpentTotals(): Response + { return $this->connector->send(new GetTimeSpentTotalsRequest($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); } } diff --git a/src/Resource/User.php b/src/Resource/User.php index ea54af9..bc24856 100644 --- a/src/Resource/User.php +++ b/src/Resource/User.php @@ -13,14 +13,11 @@ class User extends BaseResource { - public function getUsers(?string $filterexternalId = null, ?string $filterexternalIdeq = null): Response + public function getUsers(): Response { return $this->connector->send(new GetUsersRequest($filterexternalId, $filterexternalIdeq)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function postUsers(Model|array|null $data = null): Response { return $this->connector->send(new PostUsersRequest($data)); @@ -36,9 +33,6 @@ public function deleteUser(string $userId): Response return $this->connector->send(new DeleteUserRequest($userId)); } - /** - * @param Timatic\SDK\Hydration\Model|array|null $data Request data - */ public function patchUser(string $userId, Model|array|null $data = null): Response { return $this->connector->send(new PatchUserRequest($userId, $data)); diff --git a/src/Resource/UserCustomerHoursAggregate.php b/src/Resource/UserCustomerHoursAggregate.php index 99cedfb..6d2c3cd 100644 --- a/src/Resource/UserCustomerHoursAggregate.php +++ b/src/Resource/UserCustomerHoursAggregate.php @@ -8,40 +8,8 @@ class UserCustomerHoursAggregate extends BaseResource { - public function getUserCustomerHoursAggregates( - ?string $filterstartedAt = null, - ?string $filterstartedAteq = null, - ?string $filterstartedAtnq = null, - ?string $filterstartedAtgt = null, - ?string $filterstartedAtlt = null, - ?string $filterstartedAtgte = null, - ?string $filterstartedAtlte = null, - ?string $filterstartedAtcontains = null, - ?string $filterendedAt = null, - ?string $filterendedAteq = null, - ?string $filterendedAtnq = null, - ?string $filterendedAtgt = null, - ?string $filterendedAtlt = null, - ?string $filterendedAtgte = null, - ?string $filterendedAtlte = null, - ?string $filterendedAtcontains = null, - ?string $filterteamId = null, - ?string $filterteamIdeq = null, - ?string $filterteamIdnq = null, - ?string $filterteamIdgt = null, - ?string $filterteamIdlt = null, - ?string $filterteamIdgte = null, - ?string $filterteamIdlte = null, - ?string $filterteamIdcontains = null, - ?string $filteruserId = null, - ?string $filteruserIdeq = null, - ?string $filteruserIdnq = null, - ?string $filteruserIdgt = null, - ?string $filteruserIdlt = null, - ?string $filteruserIdgte = null, - ?string $filteruserIdlte = null, - ?string $filteruserIdcontains = null, - ): Response { + public function getUserCustomerHoursAggregates(): Response + { return $this->connector->send(new GetUserCustomerHoursAggregatesRequest($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); } } From 1af0ebf662e27fb1654bbd5baeb8d0f71ba96951 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 24 Nov 2025 09:31:42 +0100 Subject: [PATCH 60/62] remove support for resources --- generator/generate.php | 13 -- src/Facades/Timatic.php | 9 -- src/Resource/Approve.php | 16 --- src/Resource/Budget.php | 40 ------ src/Resource/BudgetTimeSpentTotal.php | 15 -- src/Resource/BudgetType.php | 15 -- src/Resource/Change.php | 21 --- src/Resource/Correction.php | 22 --- src/Resource/Customer.php | 40 ------ src/Resource/DailyProgress.php | 15 -- src/Resource/EntriesExport.php | 15 -- src/Resource/Entry.php | 40 ------ src/Resource/EntrySuggestion.php | 27 ---- src/Resource/Event.php | 16 --- src/Resource/ExportMail.php | 15 -- src/Resource/Incident.php | 21 --- src/Resource/MarkAsExported.php | 16 --- src/Resource/MarkAsInvoiced.php | 16 --- src/Resource/Me.php | 15 -- src/Resource/Number.php | 15 -- src/Resource/Overtime.php | 15 -- src/Resource/Period.php | 15 -- src/Resource/Team.php | 40 ------ src/Resource/TimeSpentTotal.php | 15 -- src/Resource/User.php | 40 ------ src/Resource/UserCustomerHoursAggregate.php | 15 -- src/TimaticConnector.php | 144 -------------------- 27 files changed, 686 deletions(-) delete mode 100644 src/Resource/Approve.php delete mode 100644 src/Resource/Budget.php delete mode 100644 src/Resource/BudgetTimeSpentTotal.php delete mode 100644 src/Resource/BudgetType.php delete mode 100644 src/Resource/Change.php delete mode 100644 src/Resource/Correction.php delete mode 100644 src/Resource/Customer.php delete mode 100644 src/Resource/DailyProgress.php delete mode 100644 src/Resource/EntriesExport.php delete mode 100644 src/Resource/Entry.php delete mode 100644 src/Resource/EntrySuggestion.php delete mode 100644 src/Resource/Event.php delete mode 100644 src/Resource/ExportMail.php delete mode 100644 src/Resource/Incident.php delete mode 100644 src/Resource/MarkAsExported.php delete mode 100644 src/Resource/MarkAsInvoiced.php delete mode 100644 src/Resource/Me.php delete mode 100644 src/Resource/Number.php delete mode 100644 src/Resource/Overtime.php delete mode 100644 src/Resource/Period.php delete mode 100644 src/Resource/Team.php delete mode 100644 src/Resource/TimeSpentTotal.php delete mode 100644 src/Resource/User.php delete mode 100644 src/Resource/UserCustomerHoursAggregate.php diff --git a/generator/generate.php b/generator/generate.php index 6ae785b..e0f421b 100644 --- a/generator/generate.php +++ b/generator/generate.php @@ -108,19 +108,6 @@ function writeFile($file, $outputDir, $namespace) return $filePath; } -// Write connector -if ($result->connectorClass) { - $path = writeFile($result->connectorClass, $outputDir, $config->namespace); - echo "āœ“ Connector: {$path}\n"; -} - -// Post-process and write resources -echo "\nšŸ“¦ Resources:\n"; -foreach ($result->resourceClasses as $resourceClass) { - $path = writeFile($resourceClass, $outputDir, $config->namespace); - echo ' āœ“ '.basename($path)."\n"; -} - // Write requests echo "\nšŸ“ Requests:\n"; foreach ($result->requestClasses as $requestClass) { diff --git a/src/Facades/Timatic.php b/src/Facades/Timatic.php index 7e938fb..79b5d5b 100644 --- a/src/Facades/Timatic.php +++ b/src/Facades/Timatic.php @@ -7,15 +7,6 @@ use Illuminate\Support\Facades\Facade; /** - * @method static \Timatic\SDK\Resource\Budget budget() - * @method static \Timatic\SDK\Resource\Customer customer() - * @method static \Timatic\SDK\Resource\User user() - * @method static \Timatic\SDK\Resource\Team team() - * @method static \Timatic\SDK\Resource\Entry entry() - * @method static \Timatic\SDK\Resource\Incident incident() - * @method static \Timatic\SDK\Resource\Change change() - * @method static \Timatic\SDK\Resource\Overtime overtime() - * @method static \Timatic\SDK\Resource\Event event() * @method static \Saloon\Http\Response send(\Saloon\Http\Request $request) * @method static \Timatic\SDK\TimaticConnector withMockClient(\Saloon\Http\Faking\MockClient $mockClient) * @method static \Timatic\SDK\TimaticConnector headers() diff --git a/src/Resource/Approve.php b/src/Resource/Approve.php deleted file mode 100644 index 3b47749..0000000 --- a/src/Resource/Approve.php +++ /dev/null @@ -1,16 +0,0 @@ -connector->send(new PostOvertimeApproveRequest($overtimeId, $data)); - } -} diff --git a/src/Resource/Budget.php b/src/Resource/Budget.php deleted file mode 100644 index 362f798..0000000 --- a/src/Resource/Budget.php +++ /dev/null @@ -1,40 +0,0 @@ -connector->send(new GetBudgetsRequest($filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterbudgetTypeId, $filterbudgetTypeIdeq, $filterbudgetTypeIdnq, $filterbudgetTypeIdgt, $filterbudgetTypeIdlt, $filterbudgetTypeIdgte, $filterbudgetTypeIdlte, $filterbudgetTypeIdcontains, $filterisArchived, $filtercustomerExternalId, $filtershowToCustomer, $include)); - } - - public function postBudgets(Model|array|null $data = null): Response - { - return $this->connector->send(new PostBudgetsRequest($data)); - } - - public function getBudget(string $budgetId): Response - { - return $this->connector->send(new GetBudgetRequest($budgetId)); - } - - public function deleteBudget(string $budgetId): Response - { - return $this->connector->send(new DeleteBudgetRequest($budgetId)); - } - - public function patchBudget(string $budgetId, Model|array|null $data = null): Response - { - return $this->connector->send(new PatchBudgetRequest($budgetId, $data)); - } -} diff --git a/src/Resource/BudgetTimeSpentTotal.php b/src/Resource/BudgetTimeSpentTotal.php deleted file mode 100644 index 130a9ba..0000000 --- a/src/Resource/BudgetTimeSpentTotal.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetBudgetTimeSpentTotalsRequest($filterbudgetId, $filterbudgetIdeq)); - } -} diff --git a/src/Resource/BudgetType.php b/src/Resource/BudgetType.php deleted file mode 100644 index 9d73620..0000000 --- a/src/Resource/BudgetType.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetBudgetTypesRequest); - } -} diff --git a/src/Resource/Change.php b/src/Resource/Change.php deleted file mode 100644 index c824f8e..0000000 --- a/src/Resource/Change.php +++ /dev/null @@ -1,21 +0,0 @@ -connector->send(new GetChangeRequest($changeId)); - } - - public function getChanges(): Response - { - return $this->connector->send(new GetChangesRequest); - } -} diff --git a/src/Resource/Correction.php b/src/Resource/Correction.php deleted file mode 100644 index 13fe514..0000000 --- a/src/Resource/Correction.php +++ /dev/null @@ -1,22 +0,0 @@ -connector->send(new PostCorrectionsRequest($data)); - } - - public function patchCorrection(string $correctionId, Model|array|null $data = null): Response - { - return $this->connector->send(new PatchCorrectionRequest($correctionId, $data)); - } -} diff --git a/src/Resource/Customer.php b/src/Resource/Customer.php deleted file mode 100644 index 610ac2c..0000000 --- a/src/Resource/Customer.php +++ /dev/null @@ -1,40 +0,0 @@ -connector->send(new GetCustomersRequest($filterexternalId, $filterexternalIdeq)); - } - - public function postCustomers(Model|array|null $data = null): Response - { - return $this->connector->send(new PostCustomersRequest($data)); - } - - public function getCustomer(string $customerId): Response - { - return $this->connector->send(new GetCustomerRequest($customerId)); - } - - public function deleteCustomer(string $customerId): Response - { - return $this->connector->send(new DeleteCustomerRequest($customerId)); - } - - public function patchCustomer(string $customerId, Model|array|null $data = null): Response - { - return $this->connector->send(new PatchCustomerRequest($customerId, $data)); - } -} diff --git a/src/Resource/DailyProgress.php b/src/Resource/DailyProgress.php deleted file mode 100644 index bac7243..0000000 --- a/src/Resource/DailyProgress.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetDailyProgressesRequest); - } -} diff --git a/src/Resource/EntriesExport.php b/src/Resource/EntriesExport.php deleted file mode 100644 index 3c55fd6..0000000 --- a/src/Resource/EntriesExport.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetBudgetEntriesExportRequest($budgetId, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); - } -} diff --git a/src/Resource/Entry.php b/src/Resource/Entry.php deleted file mode 100644 index 971b44b..0000000 --- a/src/Resource/Entry.php +++ /dev/null @@ -1,40 +0,0 @@ -connector->send(new GetEntriesRequest($filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains, $filterbudgetId, $filterbudgetIdeq, $filterbudgetIdnq, $filterbudgetIdgt, $filterbudgetIdlt, $filterbudgetIdgte, $filterbudgetIdlte, $filterbudgetIdcontains, $filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterhasOvertime, $filterhasOvertimeeq, $filterhasOvertimenq, $filterhasOvertimegt, $filterhasOvertimelt, $filterhasOvertimegte, $filterhasOvertimelte, $filterhasOvertimecontains, $filteruserFullName, $filteruserFullNameeq, $filteruserFullNamenq, $filteruserFullNamegt, $filteruserFullNamelt, $filteruserFullNamegte, $filteruserFullNamelte, $filteruserFullNamecontains, $filtercustomerId, $filtercustomerIdeq, $filtercustomerIdnq, $filtercustomerIdgt, $filtercustomerIdlt, $filtercustomerIdgte, $filtercustomerIdlte, $filtercustomerIdcontains, $filterticketNumber, $filterticketNumbereq, $filterticketNumbernq, $filterticketNumbergt, $filterticketNumberlt, $filterticketNumbergte, $filterticketNumberlte, $filterticketNumbercontains, $filtersettlement, $filterisInvoiced, $filterisInvoiceable, $include)); - } - - public function postEntries(Model|array|null $data = null): Response - { - return $this->connector->send(new PostEntriesRequest($data)); - } - - public function getEntry(string $entryId): Response - { - return $this->connector->send(new GetEntryRequest($entryId)); - } - - public function deleteEntry(string $entryId): Response - { - return $this->connector->send(new DeleteEntryRequest($entryId)); - } - - public function patchEntry(string $entryId, Model|array|null $data = null): Response - { - return $this->connector->send(new PatchEntryRequest($entryId, $data)); - } -} diff --git a/src/Resource/EntrySuggestion.php b/src/Resource/EntrySuggestion.php deleted file mode 100644 index ced6e0e..0000000 --- a/src/Resource/EntrySuggestion.php +++ /dev/null @@ -1,27 +0,0 @@ -connector->send(new GetEntrySuggestionsRequest($filterdate, $filterdateeq, $filterdatenq, $filterdategt, $filterdatelt, $filterdategte, $filterdatelte, $filterdatecontains)); - } - - public function getEntrySuggestion(string $entrySuggestionId): Response - { - return $this->connector->send(new GetEntrySuggestionRequest($entrySuggestionId)); - } - - public function deleteEntrySuggestion(string $entrySuggestionId): Response - { - return $this->connector->send(new DeleteEntrySuggestionRequest($entrySuggestionId)); - } -} diff --git a/src/Resource/Event.php b/src/Resource/Event.php deleted file mode 100644 index a7a2eec..0000000 --- a/src/Resource/Event.php +++ /dev/null @@ -1,16 +0,0 @@ -connector->send(new PostEventsRequest($data)); - } -} diff --git a/src/Resource/ExportMail.php b/src/Resource/ExportMail.php deleted file mode 100644 index e3ee791..0000000 --- a/src/Resource/ExportMail.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetBudgetsExportMailsRequest); - } -} diff --git a/src/Resource/Incident.php b/src/Resource/Incident.php deleted file mode 100644 index 71598fc..0000000 --- a/src/Resource/Incident.php +++ /dev/null @@ -1,21 +0,0 @@ -connector->send(new GetIncidentRequest($incidentId)); - } - - public function getIncidents(): Response - { - return $this->connector->send(new GetIncidentsRequest); - } -} diff --git a/src/Resource/MarkAsExported.php b/src/Resource/MarkAsExported.php deleted file mode 100644 index 24cf1ed..0000000 --- a/src/Resource/MarkAsExported.php +++ /dev/null @@ -1,16 +0,0 @@ -connector->send(new PostOvertimeMarkAsExportedRequest($overtimeId, $data)); - } -} diff --git a/src/Resource/MarkAsInvoiced.php b/src/Resource/MarkAsInvoiced.php deleted file mode 100644 index b0ed769..0000000 --- a/src/Resource/MarkAsInvoiced.php +++ /dev/null @@ -1,16 +0,0 @@ -connector->send(new PostEntryMarkAsInvoicedRequest($entryId, $data)); - } -} diff --git a/src/Resource/Me.php b/src/Resource/Me.php deleted file mode 100644 index f5a933f..0000000 --- a/src/Resource/Me.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetMesRequest); - } -} diff --git a/src/Resource/Number.php b/src/Resource/Number.php deleted file mode 100644 index 77c906e..0000000 --- a/src/Resource/Number.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetIncidentsNumberRequest($incidentId)); - } -} diff --git a/src/Resource/Overtime.php b/src/Resource/Overtime.php deleted file mode 100644 index 4a9b49e..0000000 --- a/src/Resource/Overtime.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetOvertimesRequest($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterisApproved, $filterapprovedAt, $filterapprovedAteq, $filterapprovedAtnq, $filterapprovedAtgt, $filterapprovedAtlt, $filterapprovedAtgte, $filterapprovedAtlte, $filterapprovedAtcontains, $filterisExported)); - } -} diff --git a/src/Resource/Period.php b/src/Resource/Period.php deleted file mode 100644 index 56d8c3e..0000000 --- a/src/Resource/Period.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetBudgetPeriodsRequest($budgetId)); - } -} diff --git a/src/Resource/Team.php b/src/Resource/Team.php deleted file mode 100644 index 1c08479..0000000 --- a/src/Resource/Team.php +++ /dev/null @@ -1,40 +0,0 @@ -connector->send(new GetTeamsRequest); - } - - public function postTeams(Model|array|null $data = null): Response - { - return $this->connector->send(new PostTeamsRequest($data)); - } - - public function getTeam(string $teamId): Response - { - return $this->connector->send(new GetTeamRequest($teamId)); - } - - public function deleteTeam(string $teamId): Response - { - return $this->connector->send(new DeleteTeamRequest($teamId)); - } - - public function patchTeam(string $teamId, Model|array|null $data = null): Response - { - return $this->connector->send(new PatchTeamRequest($teamId, $data)); - } -} diff --git a/src/Resource/TimeSpentTotal.php b/src/Resource/TimeSpentTotal.php deleted file mode 100644 index f82664b..0000000 --- a/src/Resource/TimeSpentTotal.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetTimeSpentTotalsRequest($filterstartedAtgte, $filterstartedAtlte, $filterteamId, $filterteamIdeq, $filteruserId, $filteruserIdeq)); - } -} diff --git a/src/Resource/User.php b/src/Resource/User.php deleted file mode 100644 index bc24856..0000000 --- a/src/Resource/User.php +++ /dev/null @@ -1,40 +0,0 @@ -connector->send(new GetUsersRequest($filterexternalId, $filterexternalIdeq)); - } - - public function postUsers(Model|array|null $data = null): Response - { - return $this->connector->send(new PostUsersRequest($data)); - } - - public function getUser(string $userId): Response - { - return $this->connector->send(new GetUserRequest($userId)); - } - - public function deleteUser(string $userId): Response - { - return $this->connector->send(new DeleteUserRequest($userId)); - } - - public function patchUser(string $userId, Model|array|null $data = null): Response - { - return $this->connector->send(new PatchUserRequest($userId, $data)); - } -} diff --git a/src/Resource/UserCustomerHoursAggregate.php b/src/Resource/UserCustomerHoursAggregate.php deleted file mode 100644 index 6d2c3cd..0000000 --- a/src/Resource/UserCustomerHoursAggregate.php +++ /dev/null @@ -1,15 +0,0 @@ -connector->send(new GetUserCustomerHoursAggregatesRequest($filterstartedAt, $filterstartedAteq, $filterstartedAtnq, $filterstartedAtgt, $filterstartedAtlt, $filterstartedAtgte, $filterstartedAtlte, $filterstartedAtcontains, $filterendedAt, $filterendedAteq, $filterendedAtnq, $filterendedAtgt, $filterendedAtlt, $filterendedAtgte, $filterendedAtlte, $filterendedAtcontains, $filterteamId, $filterteamIdeq, $filterteamIdnq, $filterteamIdgt, $filterteamIdlt, $filterteamIdgte, $filterteamIdlte, $filterteamIdcontains, $filteruserId, $filteruserIdeq, $filteruserIdnq, $filteruserIdgt, $filteruserIdlt, $filteruserIdgte, $filteruserIdlte, $filteruserIdcontains)); - } -} diff --git a/src/TimaticConnector.php b/src/TimaticConnector.php index 2022f9d..f650392 100644 --- a/src/TimaticConnector.php +++ b/src/TimaticConnector.php @@ -6,30 +6,6 @@ use Saloon\Http\Request; use Saloon\PaginationPlugin\Contracts\HasPagination; use Timatic\SDK\Pagination\JsonApiPaginator; -use Timatic\SDK\Resource\Approve; -use Timatic\SDK\Resource\Budget; -use Timatic\SDK\Resource\BudgetTimeSpentTotal; -use Timatic\SDK\Resource\BudgetType; -use Timatic\SDK\Resource\Change; -use Timatic\SDK\Resource\Correction; -use Timatic\SDK\Resource\Customer; -use Timatic\SDK\Resource\DailyProgress; -use Timatic\SDK\Resource\EntriesExport; -use Timatic\SDK\Resource\Entry; -use Timatic\SDK\Resource\EntrySuggestion; -use Timatic\SDK\Resource\Event; -use Timatic\SDK\Resource\ExportMail; -use Timatic\SDK\Resource\Incident; -use Timatic\SDK\Resource\MarkAsExported; -use Timatic\SDK\Resource\MarkAsInvoiced; -use Timatic\SDK\Resource\Me; -use Timatic\SDK\Resource\Number; -use Timatic\SDK\Resource\Overtime; -use Timatic\SDK\Resource\Period; -use Timatic\SDK\Resource\Team; -use Timatic\SDK\Resource\TimeSpentTotal; -use Timatic\SDK\Resource\User; -use Timatic\SDK\Resource\UserCustomerHoursAggregate; use Timatic\SDK\Responses\TimaticResponse; /** @@ -67,124 +43,4 @@ public function resolveBaseUrl(): string { return config('timatic.base_url'); } - - public function approve(): Approve - { - return new Approve($this); - } - - public function budget(): Budget - { - return new Budget($this); - } - - public function budgetTimeSpentTotal(): BudgetTimeSpentTotal - { - return new BudgetTimeSpentTotal($this); - } - - public function budgetType(): BudgetType - { - return new BudgetType($this); - } - - public function change(): Change - { - return new Change($this); - } - - public function correction(): Correction - { - return new Correction($this); - } - - public function customer(): Customer - { - return new Customer($this); - } - - public function dailyProgress(): DailyProgress - { - return new DailyProgress($this); - } - - public function entriesExport(): EntriesExport - { - return new EntriesExport($this); - } - - public function entry(): Entry - { - return new Entry($this); - } - - public function entrySuggestion(): EntrySuggestion - { - return new EntrySuggestion($this); - } - - public function event(): Event - { - return new Event($this); - } - - public function exportMail(): ExportMail - { - return new ExportMail($this); - } - - public function incident(): Incident - { - return new Incident($this); - } - - public function markAsExported(): MarkAsExported - { - return new MarkAsExported($this); - } - - public function markAsInvoiced(): MarkAsInvoiced - { - return new MarkAsInvoiced($this); - } - - public function me(): Me - { - return new Me($this); - } - - public function number(): Number - { - return new Number($this); - } - - public function overtime(): Overtime - { - return new Overtime($this); - } - - public function period(): Period - { - return new Period($this); - } - - public function team(): Team - { - return new Team($this); - } - - public function timeSpentTotal(): TimeSpentTotal - { - return new TimeSpentTotal($this); - } - - public function user(): User - { - return new User($this); - } - - public function userCustomerHoursAggregate(): UserCustomerHoursAggregate - { - return new UserCustomerHoursAggregate($this); - } } From eb4fdc2e41be91fa22ef4c22f9357c37b5b96dd8 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 24 Nov 2025 09:28:52 +0100 Subject: [PATCH 61/62] use requests instead of resources in tests --- .../TestGenerators/Traits/MockJsonDataTrait.php | 15 +++++++-------- .../stubs/pest-collection-request-test-func.stub | 6 +++--- .../stubs/pest-delete-request-test-func.stub | 3 ++- .../stubs/pest-mutation-test-func.stub | 6 ++++-- .../pest-singular-get-request-test-func.stub | 3 ++- tests/Requests/ApproveTest.php | 4 +++- tests/Requests/BudgetTest.php | 14 ++++++++++---- tests/Requests/CorrectionTest.php | 8 ++++++-- tests/Requests/CustomerTest.php | 14 ++++++++++---- tests/Requests/EntrySuggestionTest.php | 6 ++++-- tests/Requests/EntryTest.php | 14 ++++++++++---- tests/Requests/EventTest.php | 4 +++- tests/Requests/MarkAsExportedTest.php | 4 +++- tests/Requests/MarkAsInvoicedTest.php | 4 +++- tests/Requests/TeamTest.php | 14 ++++++++++---- tests/Requests/UserTest.php | 14 ++++++++++---- 16 files changed, 90 insertions(+), 43 deletions(-) diff --git a/generator/TestGenerators/Traits/MockJsonDataTrait.php b/generator/TestGenerators/Traits/MockJsonDataTrait.php index e2e7560..41ac9f3 100644 --- a/generator/TestGenerators/Traits/MockJsonDataTrait.php +++ b/generator/TestGenerators/Traits/MockJsonDataTrait.php @@ -7,25 +7,24 @@ trait MockJsonDataTrait /** * Format an array as PHP code string */ - protected function formatArrayAsPhp(array $data, int $indent = 0): string + protected function formatArrayAsPhp(array $data): string { - $indentStr = str_repeat(' ', $indent); $lines = []; foreach ($data as $key => $value) { $keyStr = is_string($key) ? "'$key'" : $key; if (is_array($value)) { - $lines[] = $indentStr." $keyStr => ".$this->formatArrayAsPhp($value, $indent + 1).','; + $lines[] = "$keyStr => ".$this->formatArrayAsPhp($value).','; } elseif (is_string($value)) { $escapedValue = addslashes($value); - $lines[] = $indentStr." $keyStr => '$escapedValue',"; + $lines[] = "$keyStr => '$escapedValue',"; } elseif (is_bool($value)) { - $lines[] = $indentStr." $keyStr => ".($value ? 'true' : 'false').','; + $lines[] = "$keyStr => ".($value ? 'true' : 'false').','; } elseif (is_null($value)) { - $lines[] = $indentStr." $keyStr => null,"; + $lines[] = "$keyStr => null,"; } else { - $lines[] = $indentStr." $keyStr => $value,"; + $lines[] = "$keyStr => $value,"; } } @@ -33,6 +32,6 @@ protected function formatArrayAsPhp(array $data, int $indent = 0): string return '[]'; } - return "[\n".implode("\n", $lines)."\n$indentStr]"; + return "[\n".implode("\n", $lines)."\n]"; } } diff --git a/generator/TestGenerators/stubs/pest-collection-request-test-func.stub b/generator/TestGenerators/stubs/pest-collection-request-test-func.stub index 5f4518c..1d260a4 100644 --- a/generator/TestGenerators/stubs/pest-collection-request-test-func.stub +++ b/generator/TestGenerators/stubs/pest-collection-request-test-func.stub @@ -4,17 +4,17 @@ it('{{ testDescription }}', function () { ]); $request = (new {{ requestClass }}({{ nonFilterParams }})) -{{ filterChain }}; + {{ filterChain }}; $response = $this->{{ clientName }}->send($request); Saloon::assertSent({{ requestClass }}::class); -{{ filterAssertionBlock }} + {{ filterAssertionBlock }} expect($response->status())->toBe(200); $dtoCollection = $response->dto(); expect($dtoCollection->first()) -{{ dtoAssertions }}; + {{ dtoAssertions }}; }); diff --git a/generator/TestGenerators/stubs/pest-delete-request-test-func.stub b/generator/TestGenerators/stubs/pest-delete-request-test-func.stub index 8919a7b..30f5041 100644 --- a/generator/TestGenerators/stubs/pest-delete-request-test-func.stub +++ b/generator/TestGenerators/stubs/pest-delete-request-test-func.stub @@ -3,7 +3,8 @@ it('{{ testDescription }}', function () { {{ requestClass }}::class => MockResponse::make([], 200), ]); - $response = $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ methodArguments }}); + $request = new {{ requestClass }}({{ methodArguments }}); + $response = $this->{{ clientName }}->send($request); Saloon::assertSent({{ requestClass }}::class); diff --git a/generator/TestGenerators/stubs/pest-mutation-test-func.stub b/generator/TestGenerators/stubs/pest-mutation-test-func.stub index dfb3b58..a088910 100644 --- a/generator/TestGenerators/stubs/pest-mutation-test-func.stub +++ b/generator/TestGenerators/stubs/pest-mutation-test-func.stub @@ -4,9 +4,11 @@ it('{{ testDescription }}', function () { ]); // Create DTO with sample data -{{ dtoInstantiation }} + {{ dtoInstantiation }} + + $request = new {{ requestClass }}({{ mutationMethodArguments }}); + $this->{{ clientName }}->send($request); - $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ mutationMethodArguments }}); Saloon::assertSent({{ requestClass }}::class); {{ bodyValidation }} diff --git a/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub b/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub index ec99f1c..45ea19d 100644 --- a/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub +++ b/generator/TestGenerators/stubs/pest-singular-get-request-test-func.stub @@ -3,7 +3,8 @@ it('{{ testDescription }}', function () { {{ requestClass }}::class => {{ mockResponse }}, ]); - $response = $this->{{ clientName }}->{{ resourceName }}()->{{ methodName }}({{ methodArguments }}); + $request = new {{ requestClass }}({{ methodArguments }}); + $response = $this->{{ clientName }}->send($request); Saloon::assertSent({{ requestClass }}::class); diff --git a/tests/Requests/ApproveTest.php b/tests/Requests/ApproveTest.php index 37562fd..276517a 100644 --- a/tests/Requests/ApproveTest.php +++ b/tests/Requests/ApproveTest.php @@ -21,7 +21,9 @@ $dto->startedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); $dto->endedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); - $this->timaticConnector->approve()->postOvertimeApprove(overtimeId: 'test string', data: $dto); + $request = new PostOvertimeApproveRequest(overtimeId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostOvertimeApproveRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/BudgetTest.php b/tests/Requests/BudgetTest.php index 0e826f8..898feca 100644 --- a/tests/Requests/BudgetTest.php +++ b/tests/Requests/BudgetTest.php @@ -115,7 +115,9 @@ $dto->showToCustomer = true; $dto->changeId = 'change_id-123'; - $this->timaticConnector->budget()->postBudgets($dto); + $request = new PostBudgetsRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostBudgetsRequest::class); $mockClient->assertSent(function (Request $request) { @@ -159,9 +161,10 @@ ], 200), ]); - $response = $this->timaticConnector->budget()->getBudget( + $request = new GetBudgetRequest( budgetId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetBudgetRequest::class); @@ -191,9 +194,10 @@ DeleteBudgetRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->budget()->deleteBudget( + $request = new DeleteBudgetRequest( budgetId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(DeleteBudgetRequest::class); @@ -212,7 +216,9 @@ $dto->showToCustomer = true; $dto->changeId = 'change_id-123'; - $this->timaticConnector->budget()->patchBudget(budgetId: 'test string', data: $dto); + $request = new PatchBudgetRequest(budgetId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PatchBudgetRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/CorrectionTest.php b/tests/Requests/CorrectionTest.php index bd0b464..5c76d63 100644 --- a/tests/Requests/CorrectionTest.php +++ b/tests/Requests/CorrectionTest.php @@ -19,7 +19,9 @@ $dto = new \Timatic\SDK\Dto\Correction; $dto->name = 'test value'; - $this->timaticConnector->correction()->postCorrections($dto); + $request = new PostCorrectionsRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostCorrectionsRequest::class); $mockClient->assertSent(function (Request $request) { @@ -40,7 +42,9 @@ $dto = new \Timatic\SDK\Dto\Correction; $dto->name = 'test value'; - $this->timaticConnector->correction()->patchCorrection(correctionId: 'test string', data: $dto); + $request = new PatchCorrectionRequest(correctionId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PatchCorrectionRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/CustomerTest.php b/tests/Requests/CustomerTest.php index a4f55d1..84803d3 100644 --- a/tests/Requests/CustomerTest.php +++ b/tests/Requests/CustomerTest.php @@ -80,7 +80,9 @@ $dto->hourlyRate = 'test value'; $dto->accountManagerUserId = 'account_manager_user_id-123'; - $this->timaticConnector->customer()->postCustomers($dto); + $request = new PostCustomersRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostCustomersRequest::class); $mockClient->assertSent(function (Request $request) { @@ -114,9 +116,10 @@ ], 200), ]); - $response = $this->timaticConnector->customer()->getCustomer( + $request = new GetCustomerRequest( customerId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetCustomerRequest::class); @@ -136,9 +139,10 @@ DeleteCustomerRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->customer()->deleteCustomer( + $request = new DeleteCustomerRequest( customerId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(DeleteCustomerRequest::class); @@ -157,7 +161,9 @@ $dto->hourlyRate = 'test value'; $dto->accountManagerUserId = 'account_manager_user_id-123'; - $this->timaticConnector->customer()->patchCustomer(customerId: 'test string', data: $dto); + $request = new PatchCustomerRequest(customerId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PatchCustomerRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/EntrySuggestionTest.php b/tests/Requests/EntrySuggestionTest.php index 88d8dbc..6d2d2b7 100644 --- a/tests/Requests/EntrySuggestionTest.php +++ b/tests/Requests/EntrySuggestionTest.php @@ -98,9 +98,10 @@ ], 200), ]); - $response = $this->timaticConnector->entrySuggestion()->getEntrySuggestion( + $request = new GetEntrySuggestionRequest( entrySuggestionId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetEntrySuggestionRequest::class); @@ -124,9 +125,10 @@ DeleteEntrySuggestionRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->entrySuggestion()->deleteEntrySuggestion( + $request = new DeleteEntrySuggestionRequest( entrySuggestionId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(DeleteEntrySuggestionRequest::class); diff --git a/tests/Requests/EntryTest.php b/tests/Requests/EntryTest.php index 75ea64c..4b9889b 100644 --- a/tests/Requests/EntryTest.php +++ b/tests/Requests/EntryTest.php @@ -148,7 +148,9 @@ $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; - $this->timaticConnector->entry()->postEntries($dto); + $request = new PostEntriesRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostEntriesRequest::class); $mockClient->assertSent(function (Request $request) { @@ -203,9 +205,10 @@ ], 200), ]); - $response = $this->timaticConnector->entry()->getEntry( + $request = new GetEntryRequest( entryId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetEntryRequest::class); @@ -246,9 +249,10 @@ DeleteEntryRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->entry()->deleteEntry( + $request = new DeleteEntryRequest( entryId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(DeleteEntryRequest::class); @@ -267,7 +271,9 @@ $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; - $this->timaticConnector->entry()->patchEntry(entryId: 'test string', data: $dto); + $request = new PatchEntryRequest(entryId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PatchEntryRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/EventTest.php b/tests/Requests/EventTest.php index 3dde365..6ca98b6 100644 --- a/tests/Requests/EventTest.php +++ b/tests/Requests/EventTest.php @@ -21,7 +21,9 @@ $dto->ticketId = 'ticket_id-123'; $dto->sourceId = 'source_id-123'; - $this->timaticConnector->event()->postEvents($dto); + $request = new PostEventsRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostEventsRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/MarkAsExportedTest.php b/tests/Requests/MarkAsExportedTest.php index a396f4c..94e238e 100644 --- a/tests/Requests/MarkAsExportedTest.php +++ b/tests/Requests/MarkAsExportedTest.php @@ -21,7 +21,9 @@ $dto->startedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); $dto->endedAt = \Carbon\Carbon::parse('2025-01-15T10:30:00Z'); - $this->timaticConnector->markAsExported()->postOvertimeMarkAsExported(overtimeId: 'test string', data: $dto); + $request = new PostOvertimeMarkAsExportedRequest(overtimeId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostOvertimeMarkAsExportedRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/MarkAsInvoicedTest.php b/tests/Requests/MarkAsInvoicedTest.php index b5abbe0..0982dd2 100644 --- a/tests/Requests/MarkAsInvoicedTest.php +++ b/tests/Requests/MarkAsInvoicedTest.php @@ -21,7 +21,9 @@ $dto->ticketTitle = 'test value'; $dto->ticketType = 'test value'; - $this->timaticConnector->markAsInvoiced()->postEntryMarkAsInvoiced(entryId: 'test string', data: $dto); + $request = new PostEntryMarkAsInvoicedRequest(entryId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostEntryMarkAsInvoicedRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/TeamTest.php b/tests/Requests/TeamTest.php index ac93e62..0f3b254 100644 --- a/tests/Requests/TeamTest.php +++ b/tests/Requests/TeamTest.php @@ -62,7 +62,9 @@ $dto->externalId = 'external_id-123'; $dto->name = 'test name'; - $this->timaticConnector->team()->postTeams($dto); + $request = new PostTeamsRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostTeamsRequest::class); $mockClient->assertSent(function (Request $request) { @@ -92,9 +94,10 @@ ], 200), ]); - $response = $this->timaticConnector->team()->getTeam( + $request = new GetTeamRequest( teamId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetTeamRequest::class); @@ -112,9 +115,10 @@ DeleteTeamRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->team()->deleteTeam( + $request = new DeleteTeamRequest( teamId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(DeleteTeamRequest::class); @@ -131,7 +135,9 @@ $dto->externalId = 'external_id-123'; $dto->name = 'test name'; - $this->timaticConnector->team()->patchTeam(teamId: 'test string', data: $dto); + $request = new PatchTeamRequest(teamId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PatchTeamRequest::class); $mockClient->assertSent(function (Request $request) { diff --git a/tests/Requests/UserTest.php b/tests/Requests/UserTest.php index e92a01f..30ece34 100644 --- a/tests/Requests/UserTest.php +++ b/tests/Requests/UserTest.php @@ -72,7 +72,9 @@ $dto->externalId = 'external_id-123'; $dto->email = 'test@example.com'; - $this->timaticConnector->user()->postUsers($dto); + $request = new PostUsersRequest($dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PostUsersRequest::class); $mockClient->assertSent(function (Request $request) { @@ -102,9 +104,10 @@ ], 200), ]); - $response = $this->timaticConnector->user()->getUser( + $request = new GetUserRequest( userId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(GetUserRequest::class); @@ -122,9 +125,10 @@ DeleteUserRequest::class => MockResponse::make([], 200), ]); - $response = $this->timaticConnector->user()->deleteUser( + $request = new DeleteUserRequest( userId: 'test string' ); + $response = $this->timaticConnector->send($request); Saloon::assertSent(DeleteUserRequest::class); @@ -141,7 +145,9 @@ $dto->externalId = 'external_id-123'; $dto->email = 'test@example.com'; - $this->timaticConnector->user()->patchUser(userId: 'test string', data: $dto); + $request = new PatchUserRequest(userId: 'test string', data: $dto); + $this->timaticConnector->send($request); + Saloon::assertSent(PatchUserRequest::class); $mockClient->assertSent(function (Request $request) { From e335ab79d010c606c8b4a392350213e74e2b1ad0 Mon Sep 17 00:00:00 2001 From: Tomas van Rijsse Date: Mon, 24 Nov 2025 08:14:06 +0100 Subject: [PATCH 62/62] pipeline fix --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index cd99b2e..c86395e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -39,7 +39,7 @@ jobs: run: | composer remove "crescat-io/saloon-sdk-generator" "larastan/larastan" "laravel/boost" "laravel/pint" --dev --no-interaction composer require "laravel/framework:${{ matrix.laravel }}" " saloonphp/laravel-plugin" --no-interaction --no-update - composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction + composer update --prefer-dist --no-interaction - name: Run Pest tests run: ./vendor/bin/pest