From 16e742ae7380499a62ae3ac641e1fffcf7cde8e3 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sat, 27 Jul 2024 15:42:43 +0200 Subject: [PATCH] Various API updates. --- .../Controllers/JsonApi/AccountController.php | 50 +------ app/JsonApi/V2/Accounts/AccountRepository.php | 14 +- app/JsonApi/V2/Accounts/AccountResource.php | 122 +++------------- .../V2/Accounts/AccountResourceOld.php | 135 ++++++++++++++++++ app/JsonApi/V2/Accounts/AccountSchema.php | 61 ++++---- app/JsonApi/V2/Accounts/AccountSchemaOld.php | 72 ++++++++++ .../V2/Accounts/Capabilities/AccountQuery.php | 7 +- .../Capabilities/CrudAccountRelations.php | 31 ++++ app/JsonApi/V2/Server.php | 11 +- app/Models/Transaction.php | 2 +- .../JsonApi/Concerns/UserGroupDetectable.php | 63 ++++++++ .../JsonApi/Concerns/UsergroupAware.php | 2 + .../JsonApi/ValidateSortParameters.php | 2 +- app/Support/Request/ChecksLogin.php | 1 + routes/api.php | 1 + 15 files changed, 377 insertions(+), 197 deletions(-) create mode 100644 app/JsonApi/V2/Accounts/AccountResourceOld.php create mode 100644 app/JsonApi/V2/Accounts/AccountSchemaOld.php create mode 100644 app/JsonApi/V2/Accounts/Capabilities/CrudAccountRelations.php create mode 100644 app/Support/JsonApi/Concerns/UserGroupDetectable.php diff --git a/app/Api/V2/Controllers/JsonApi/AccountController.php b/app/Api/V2/Controllers/JsonApi/AccountController.php index 734c7afd3f..cf6943048c 100644 --- a/app/Api/V2/Controllers/JsonApi/AccountController.php +++ b/app/Api/V2/Controllers/JsonApi/AccountController.php @@ -24,14 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V2\Controllers\JsonApi; use FireflyIII\Http\Controllers\Controller; -use FireflyIII\JsonApi\V2\Accounts\Capabilities\AccountQuery; -use Illuminate\Contracts\Support\Responsable; -use Illuminate\Http\Response; -use LaravelJsonApi\Contracts\Routing\Route; -use LaravelJsonApi\Contracts\Store\Store as StoreContract; -use LaravelJsonApi\Core\Responses\DataResponse; use LaravelJsonApi\Laravel\Http\Controllers\Actions; -use LaravelJsonApi\Laravel\Http\Requests\ResourceQuery; /** * Class AccountController @@ -45,7 +38,7 @@ class AccountController extends Controller use Actions\AttachRelationship; use Actions\Destroy; use Actions\DetachRelationship; - //use Actions\FetchMany; + use Actions\FetchMany; use Actions\FetchOne; use Actions\FetchRelated; use Actions\FetchRelationship; @@ -53,47 +46,6 @@ class AccountController extends Controller use Actions\Update; use Actions\UpdateRelationship; - /** - * Fetch zero to many JSON API resources. - * - * @param Route $route - * @param StoreContract $store - * @return Responsable|Response - */ - public function index(Route $route, AccountQuery $store) - { - /** - * TODO Op het moment dat je een custom repositroy wil gebruiken kan je de index overrulen zoals ik hier - * doe. Maar je moet toch ook wel de relatie, de store kunnen overrulen? Waarom gebruikt-ie daar zijn eigen? - * - * TODO dat zit hier: https://laraveljsonapi.io/docs/3.0/resources/ - * - */ - $request = ResourceQuery::queryMany( - $resourceType = $route->resourceType() - ); - - $response = null; - - if (method_exists($this, 'searching')) { - $response = $this->searching($request); - } - - if ($response) { - return $response; - } - - $data = $store - ->queryAll($resourceType) - ->withRequest($request) - ->firstOrPaginate($request->page()); - - if (method_exists($this, 'searched')) { - $response = $this->searched($data, $request); - } - - return $response ?: DataResponse::make($data)->withQueryParameters($request); - } // public function readAccountBalances(AnonymousQuery $query, AccountBalanceSchema $schema, Account $account): Responsable // { diff --git a/app/JsonApi/V2/Accounts/AccountRepository.php b/app/JsonApi/V2/Accounts/AccountRepository.php index ca3df2fe13..e6b9216db8 100644 --- a/app/JsonApi/V2/Accounts/AccountRepository.php +++ b/app/JsonApi/V2/Accounts/AccountRepository.php @@ -27,6 +27,8 @@ use FireflyIII\Models\Account; use FireflyIII\Support\JsonApi\Concerns\UsergroupAware; use LaravelJsonApi\Contracts\Store\QueriesAll; use LaravelJsonApi\NonEloquent\AbstractRepository; +use LaravelJsonApi\NonEloquent\Capabilities\CrudRelations; +use LaravelJsonApi\NonEloquent\Concerns\HasRelationsCapability; /** * Class AccountRepository @@ -41,7 +43,7 @@ use LaravelJsonApi\NonEloquent\AbstractRepository; class AccountRepository extends AbstractRepository implements QueriesAll { use UsergroupAware; - + use HasRelationsCapability; /** * SiteRepository constructor. */ @@ -49,17 +51,23 @@ class AccountRepository extends AbstractRepository implements QueriesAll public function find(string $resourceId): ?object { - die('query single?'); return Account::find((int) $resourceId); } public function queryAll(): Capabilities\AccountQuery { - die('query all?'); return Capabilities\AccountQuery::make() ->withUserGroup($this->userGroup) ->withServer($this->server) ->withSchema($this->schema) ; } + + /** + * @inheritDoc + */ + protected function relations(): CrudRelations + { + return Capabilities\CrudAccountRelations::make(); + } } diff --git a/app/JsonApi/V2/Accounts/AccountResource.php b/app/JsonApi/V2/Accounts/AccountResource.php index bbb5cceb18..bc876802b0 100644 --- a/app/JsonApi/V2/Accounts/AccountResource.php +++ b/app/JsonApi/V2/Accounts/AccountResource.php @@ -1,7 +1,5 @@ resource->id; + } + + /** * Get the resource's attributes. * - * @param null|Request $request + * @param Request|null $request + * + * @return iterable */ public function attributes($request): iterable { return [ - 'created_at' => $this->resource->created_at, - 'updated_at' => $this->resource->updated_at, - 'name' => $this->resource->name, -// 'iban' => '' === $this->resource->iban ? null : $this->resource->iban, -// 'active' => $this->resource->active, -// 'last_activity' => $this->resource->last_activity, -// 'type' => $this->resource->type, -// 'account_role' => $this->resource->account_role, - - // 'virtual_balance' => $this->resource->virtual_balance, - // 'native_balance' => $this->resource->native_balance, - // 'user' => $this->resource->user_array, - // 'balances' => [] - // - // currency - // 'currency_id' => $this->resource->currency_id, - // 'currency_code' => $this->resource->currency_code, - // 'currency_symbol' => $this->resource->currency_symbol, - // 'currency_decimal_places' => $this->resource->currency_decimal_places, - - // balance (in currency, on date) - // 'current_balance' => $this->resource->current_balance, - - // 'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces), - // 'current_balance_date' => $date->toAtomString(), - // 'notes' => $this->repository->getNoteText($account), - // 'monthly_payment_date' => $monthlyPaymentDate, - // 'credit_card_type' => $creditCardType, - // 'account_number' => $this->repository->getMetaValue($account, 'account_number'), - // 'bic' => $this->repository->getMetaValue($account, 'BIC'), - // 'opening_balance' => $openingBalance, - // 'opening_balance_date' => $openingBalanceDate, - // 'liability_type' => $liabilityType, - // 'liability_direction' => $liabilityDirection, - // 'interest' => $interest, - // 'interest_period' => $interestPeriod, - // 'current_debt' => $this->repository->getMetaValue($account, 'current_debt'), - // 'include_net_worth' => $includeNetWorth, - // 'longitude' => $longitude, - // 'latitude' => $latitude, - // 'zoom_level' => $zoomLevel, - - // 'order' => $order, - - // 'currency_id' => (string) $currency->id, - // 'currency_code' => $currency->code, - // 'currency_symbol' => $currency->symbol, - // 'currency_decimal_places' => $currency->decimal_places, - // - // 'native_currency_id' => (string) $this->default->id, - // 'native_currency_code' => $this->default->code, - // 'native_currency_symbol' => $this->default->symbol, - // 'native_currency_decimal_places' => $this->default->decimal_places, - // - // // balance: - // 'current_balance' => $balance, - // 'native_current_balance' => $nativeBalance, - // 'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(), - // - // // balance difference - // 'balance_difference' => $balanceDiff, - // 'native_balance_difference' => $nativeBalanceDiff, - // 'balance_difference_start' => $diffStart, - // 'balance_difference_end' => $diffEnd, - // - // // more meta - // 'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null, - // - // // liability stuff - // 'liability_type' => $liabilityType, - // 'liability_direction' => $liabilityDirection, - // 'interest' => $interest, - // 'interest_period' => $interestPeriod, - // 'current_debt' => $currentDebt, - // - // // object group - // 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null, - // 'object_group_order' => $objectGroupOrder, - // 'object_group_title' => $objectGroupTitle, - // 'notes' => $this->repository->getNoteText($account), - // 'monthly_payment_date' => $monthlyPaymentDate, - // 'credit_card_type' => $creditCardType, - // 'bic' => $this->repository->getMetaValue($account, 'BIC'), - // 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''), - // 'opening_balance' => $openingBalance, - // 'opening_balance_date' => $openingBalanceDate, - // 'include_net_worth' => $includeNetWorth, - // 'longitude' => $longitude, - // 'latitude' => $latitude, - // 'zoom_level' => $zoomLevel, + 'created_at' => $this->resource->created_at, + 'updated_at' => $this->resource->updated_at, + 'name' => $this->resource->name, ]; } /** * Get the resource's relationships. * - * @param null|Request $request + * @param Request|null $request + * + * @return iterable */ public function relationships($request): iterable { return [ $this->relation('user')->withData($this->resource->user), - $this->relation('currency')->withData($this->resource->transactionCurrency), - //$this->relation('account_balances')->withData($this->resource->balances), ]; } + } diff --git a/app/JsonApi/V2/Accounts/AccountResourceOld.php b/app/JsonApi/V2/Accounts/AccountResourceOld.php new file mode 100644 index 0000000000..add0e625d6 --- /dev/null +++ b/app/JsonApi/V2/Accounts/AccountResourceOld.php @@ -0,0 +1,135 @@ + $this->resource->created_at, + 'updated_at' => $this->resource->updated_at, + 'name' => $this->resource->name, +// 'iban' => '' === $this->resource->iban ? null : $this->resource->iban, +// 'active' => $this->resource->active, +// 'last_activity' => $this->resource->last_activity, +// 'type' => $this->resource->type, +// 'account_role' => $this->resource->account_role, + + // 'virtual_balance' => $this->resource->virtual_balance, + // 'native_balance' => $this->resource->native_balance, + // 'user' => $this->resource->user_array, + // 'balances' => [] + // + // currency + // 'currency_id' => $this->resource->currency_id, + // 'currency_code' => $this->resource->currency_code, + // 'currency_symbol' => $this->resource->currency_symbol, + // 'currency_decimal_places' => $this->resource->currency_decimal_places, + + // balance (in currency, on date) + // 'current_balance' => $this->resource->current_balance, + + // 'current_balance' => app('steam')->bcround(app('steam')->balance($account, $date), $decimalPlaces), + // 'current_balance_date' => $date->toAtomString(), + // 'notes' => $this->repository->getNoteText($account), + // 'monthly_payment_date' => $monthlyPaymentDate, + // 'credit_card_type' => $creditCardType, + // 'account_number' => $this->repository->getMetaValue($account, 'account_number'), + // 'bic' => $this->repository->getMetaValue($account, 'BIC'), + // 'opening_balance' => $openingBalance, + // 'opening_balance_date' => $openingBalanceDate, + // 'liability_type' => $liabilityType, + // 'liability_direction' => $liabilityDirection, + // 'interest' => $interest, + // 'interest_period' => $interestPeriod, + // 'current_debt' => $this->repository->getMetaValue($account, 'current_debt'), + // 'include_net_worth' => $includeNetWorth, + // 'longitude' => $longitude, + // 'latitude' => $latitude, + // 'zoom_level' => $zoomLevel, + + // 'order' => $order, + + // 'currency_id' => (string) $currency->id, + // 'currency_code' => $currency->code, + // 'currency_symbol' => $currency->symbol, + // 'currency_decimal_places' => $currency->decimal_places, + // + // 'native_currency_id' => (string) $this->default->id, + // 'native_currency_code' => $this->default->code, + // 'native_currency_symbol' => $this->default->symbol, + // 'native_currency_decimal_places' => $this->default->decimal_places, + // + // // balance: + // 'current_balance' => $balance, + // 'native_current_balance' => $nativeBalance, + // 'current_balance_date' => $this->getDate()->endOfDay()->toAtomString(), + // + // // balance difference + // 'balance_difference' => $balanceDiff, + // 'native_balance_difference' => $nativeBalanceDiff, + // 'balance_difference_start' => $diffStart, + // 'balance_difference_end' => $diffEnd, + // + // // more meta + // 'last_activity' => array_key_exists($id, $this->lastActivity) ? $this->lastActivity[$id]->toAtomString() : null, + // + // // liability stuff + // 'liability_type' => $liabilityType, + // 'liability_direction' => $liabilityDirection, + // 'interest' => $interest, + // 'interest_period' => $interestPeriod, + // 'current_debt' => $currentDebt, + // + // // object group + // 'object_group_id' => null !== $objectGroupId ? (string) $objectGroupId : null, + // 'object_group_order' => $objectGroupOrder, + // 'object_group_title' => $objectGroupTitle, + // 'notes' => $this->repository->getNoteText($account), + // 'monthly_payment_date' => $monthlyPaymentDate, + // 'credit_card_type' => $creditCardType, + // 'bic' => $this->repository->getMetaValue($account, 'BIC'), + // 'virtual_balance' => number_format((float) $account->virtual_balance, $decimalPlaces, '.', ''), + // 'opening_balance' => $openingBalance, + // 'opening_balance_date' => $openingBalanceDate, + // 'include_net_worth' => $includeNetWorth, + // 'longitude' => $longitude, + // 'latitude' => $latitude, + // 'zoom_level' => $zoomLevel, + ]; + } + + /** + * Get the resource's relationships. + * + * @param null|Request $request + */ + public function relationships($request): iterable + { + return [ + $this->relation('user')->withData($this->resource->user), + $this->relation('currency')->withData($this->resource->transactionCurrency), + //$this->relation('account_balances')->withData($this->resource->balances), + ]; + } +} diff --git a/app/JsonApi/V2/Accounts/AccountSchema.php b/app/JsonApi/V2/Accounts/AccountSchema.php index 130cbdda59..adac8309b1 100644 --- a/app/JsonApi/V2/Accounts/AccountSchema.php +++ b/app/JsonApi/V2/Accounts/AccountSchema.php @@ -1,72 +1,61 @@ sortable()->readOnly(), - DateTime::make('updated_at')->sortable()->readOnly(), - Str::make('name')->sortable(), -// Str::make('account_type'), -// Str::make('virtual_balance'), -// Str::make('iban'), -// Boolean::make('active'), -// Number::make('order'), + Attribute::make('name'), HasOne::make('user')->readOnly(), - //HasMany::make('account_balances'), ]; } /** - * Filters mentioned here can be used to filter the results. - * TODO write down exactly how this works. + * Get the resource filters. + * + * @return array */ public function filters(): array { return [ - WhereIdIn::make($this), + // Filter::make('id'), ]; } - /** - * Get the resource paginator. - */ - public function pagination(): ?Paginator + public function repository(): AccountRepository { - return PagePagination::make(); + $this->setUserGroup($this->server->getUsergroup()); + return AccountRepository::make() + ->withServer($this->server) + ->withSchema($this) + ->withUserGroup($this->userGroup); } + } diff --git a/app/JsonApi/V2/Accounts/AccountSchemaOld.php b/app/JsonApi/V2/Accounts/AccountSchemaOld.php new file mode 100644 index 0000000000..1e1af19655 --- /dev/null +++ b/app/JsonApi/V2/Accounts/AccountSchemaOld.php @@ -0,0 +1,72 @@ +sortable()->readOnly(), + DateTime::make('updated_at')->sortable()->readOnly(), + Str::make('name')->sortable(), +// Str::make('account_type'), +// Str::make('virtual_balance'), +// Str::make('iban'), +// Boolean::make('active'), +// Number::make('order'), + HasOne::make('user')->readOnly(), + //HasMany::make('account_balances'), + ]; + } + + /** + * Filters mentioned here can be used to filter the results. + * TODO write down exactly how this works. + */ + public function filters(): array + { + return [ + WhereIdIn::make($this), + ]; + } + + /** + * Get the resource paginator. + */ + public function pagination(): ?Paginator + { + return PagePagination::make(); + } +} diff --git a/app/JsonApi/V2/Accounts/Capabilities/AccountQuery.php b/app/JsonApi/V2/Accounts/Capabilities/AccountQuery.php index 6015b1a0a6..d78712978d 100644 --- a/app/JsonApi/V2/Accounts/Capabilities/AccountQuery.php +++ b/app/JsonApi/V2/Accounts/Capabilities/AccountQuery.php @@ -33,7 +33,7 @@ use LaravelJsonApi\Contracts\Store\HasPagination; use LaravelJsonApi\NonEloquent\Capabilities\QueryAll; use LaravelJsonApi\NonEloquent\Concerns\PaginatesEnumerables; -class AccountQuery implements HasPagination +class AccountQuery extends QueryAll implements HasPagination { use ExpandsQuery; use FiltersPagination; @@ -42,10 +42,11 @@ class AccountQuery implements HasPagination use UsergroupAware; use ValidateSortParameters; + #[\Override] /** * This method returns all accounts, given a bunch of filters and sort fields, together with pagination. */ - public function queryAll(): iterable + public function get(): iterable { // collect filters $filters = $this->queryParameters->filter(); @@ -55,7 +56,7 @@ class AccountQuery implements HasPagination $pagination = $this->filtersPagination($this->queryParameters->page()); // check if we need all accounts, regardless of pagination // This is necessary when the user wants to sort on specific params. - $needsAll = $this->validateParams('account', $sort); + $needsAll = $this->needsFullDataset('account', $sort); // start the query $query = $this->userGroup->accounts(); diff --git a/app/JsonApi/V2/Accounts/Capabilities/CrudAccountRelations.php b/app/JsonApi/V2/Accounts/Capabilities/CrudAccountRelations.php new file mode 100644 index 0000000000..d31d181377 --- /dev/null +++ b/app/JsonApi/V2/Accounts/Capabilities/CrudAccountRelations.php @@ -0,0 +1,31 @@ +detectUserGroup(); + $this->setUserGroup($res); } /** diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 9e8bf2132b..4447ed75f0 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -97,7 +97,7 @@ class Transaction extends Model use HasFactory; use ReturnsIntegerIdTrait; use SoftDeletes; - use Cachable; + //use Cachable; protected $casts = [ diff --git a/app/Support/JsonApi/Concerns/UserGroupDetectable.php b/app/Support/JsonApi/Concerns/UserGroupDetectable.php new file mode 100644 index 0000000000..bbcae641fe --- /dev/null +++ b/app/Support/JsonApi/Concerns/UserGroupDetectable.php @@ -0,0 +1,63 @@ +user(); + app('log')->debug('Now in detectUserGroup()'); + + /** @var null|UserGroup $userGroup */ + $userGroup = request()->route()?->parameter('userGroup'); + if (null === $userGroup) { + app('log')->debug('Request class has no userGroup parameter, but perhaps there is a parameter.'); + $userGroupId = (int)request()->get('user_group_id'); + if (0 === $userGroupId) { + app('log')->debug(sprintf('Request class has no user_group_id parameter, grab default from user (group #%d).', $user->user_group_id)); + $userGroupId = (int)$user->user_group_id; + } + $userGroup = UserGroup::find($userGroupId); + if (null === $userGroup) { + app('log')->error(sprintf('Request class has user_group_id (#%d), but group does not exist.', $userGroupId)); + + return null; + } + app('log')->debug('Request class has valid user_group_id.'); + } + + return $userGroup; + } +} diff --git a/app/Support/JsonApi/Concerns/UsergroupAware.php b/app/Support/JsonApi/Concerns/UsergroupAware.php index 8326d7f53b..fed57e2b22 100644 --- a/app/Support/JsonApi/Concerns/UsergroupAware.php +++ b/app/Support/JsonApi/Concerns/UsergroupAware.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Support\JsonApi\Concerns; use FireflyIII\Models\UserGroup; +use FireflyIII\User; trait UsergroupAware { @@ -45,4 +46,5 @@ trait UsergroupAware return $this; } + } diff --git a/app/Support/JsonApi/ValidateSortParameters.php b/app/Support/JsonApi/ValidateSortParameters.php index 272e858d13..83d2a52491 100644 --- a/app/Support/JsonApi/ValidateSortParameters.php +++ b/app/Support/JsonApi/ValidateSortParameters.php @@ -27,7 +27,7 @@ use LaravelJsonApi\Core\Query\SortFields; trait ValidateSortParameters { - public function validateParams(string $class, ?SortFields $params): bool + public function needsFullDataset(string $class, ?SortFields $params): bool { if (null === $params) { return false; diff --git a/app/Support/Request/ChecksLogin.php b/app/Support/Request/ChecksLogin.php index af635bac7e..9b379ccb93 100644 --- a/app/Support/Request/ChecksLogin.php +++ b/app/Support/Request/ChecksLogin.php @@ -73,6 +73,7 @@ trait ChecksLogin /** * Return the user group or NULL if none is set. * Will throw exception if invalid. + * TODO duplicated in JSONAPI code. */ public function getUserGroup(): ?UserGroup { diff --git a/routes/api.php b/routes/api.php index 534fc847a2..9f571a62f4 100644 --- a/routes/api.php +++ b/routes/api.php @@ -258,6 +258,7 @@ JsonApiRoute::server('v2')->prefix('v2') $server->resource('accounts', AccountController::class)->readOnly()->relationships(function (Relationships $relations) { $relations->hasOne('user')->readOnly(); }); +// $server->resource('accounts', AccountController::class)->readOnly(); /** * USERS