Implement user API and first tests.

This commit is contained in:
James Cole
2018-03-03 08:12:18 +01:00
parent 60339a0f6a
commit 9475fef8f6
13 changed files with 505 additions and 58 deletions

View File

@@ -29,10 +29,8 @@ use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Helpers\Filter\NegativeAmountFilter;
use FireflyIII\Helpers\Filter\PositiveAmountFilter;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Services\Internal\Update\JournalUpdateService;
use FireflyIII\Transformers\TransactionTransformer;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
@@ -41,6 +39,7 @@ use League\Fractal\Pagination\IlluminatePaginatorAdapter;
use League\Fractal\Resource\Collection as FractalCollection;
use League\Fractal\Resource\Item;
use League\Fractal\Serializer\JsonApiSerializer;
use Log;
use Preferences;
/**
@@ -176,7 +175,6 @@ class TransactionController extends Controller
* @param TransactionRequest $request
*
* @return \Illuminate\Http\JsonResponse
* @throws \FireflyIII\Exceptions\FireflyException
*/
public function store(TransactionRequest $request, JournalRepositoryInterface $repository)
{
@@ -216,19 +214,20 @@ class TransactionController extends Controller
/**
* @param TransactionRequest $request
* @param TransactionJournal $journal
* @param TransactionRequest $request
* @param JournalRepositoryInterface $repository
* @param Transaction $transaction
*
* @return \Illuminate\Http\JsonResponse
*/
public function update(TransactionRequest $request, Transaction $transaction)
public function update(TransactionRequest $request, JournalRepositoryInterface $repository, Transaction $transaction)
{
$data = $request->getAll();
$data['user'] = auth()->user()->id;
/** @var JournalUpdateService $service */
$service = app(JournalUpdateService::class);
$journal = $service->update($transaction->transactionJournal, $data);
Log::debug('Inside transaction update');
$journal = $repository->update($transaction->transactionJournal, $data);
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Requests\UserRequest;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\Transformers\UserTransformer;
use FireflyIII\User;
@@ -89,21 +90,28 @@ class UserController extends Controller
*/
public function index(Request $request)
{
$pageSize = intval(Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data);
// user preferences
$pageSize = intval(Preferences::getForUser(auth()->user(), 'listPageSize', 50)->data);
// make manager
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// build collection
$collection = $this->repository->all();
$count = $collection->count();
$users = $collection->slice(($this->parameters->get('page') - 1) * $pageSize, $pageSize);
// make paginator:
$paginator = new LengthAwarePaginator($users, $count, $pageSize, $this->parameters->get('page'));
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
$paginator->setPath(route('api.v1.users.index') . $this->buildParams());
// make resource
$resource = new FractalCollection($users, new UserTransformer($this->parameters), 'users');
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
return response()->json($manager->createData($resource)->toArray());
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
@@ -114,35 +122,70 @@ class UserController extends Controller
*/
public function show(Request $request, User $user)
{
// make manager
$manager = new Manager();
//$manager->parseIncludes(['attachments', 'journals', 'user']);
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// make resource
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
return response()->json($manager->createData($resource)->toArray());
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param AccountRequest $request
* @param UserRequest $request
*
* @return \Illuminate\Http\JsonResponse
*/
public function store(AccountRequest $request)
public function store(UserRequest $request)
{
$data = $request->getAll();
$user = $this->repository->store($data);
// make manager
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// make resource
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}
/**
* @param AccountRequest $request
* @param Account $account
* @param UserRequest $request
* @param User $user
*
* @return \Illuminate\Http\JsonResponse
*/
public function update(AccountRequest $request, Account $account)
public function update(UserRequest $request, User $user)
{
$data = $request->getAll();
$user = $this->repository->update($user, $data);
// make manager
$manager = new Manager();
$baseUrl = $request->getSchemeAndHttpHost() . '/api/v1';
$manager->setSerializer(new JsonApiSerializer($baseUrl));
// add include parameter:
$include = $request->get('include') ?? '';
$manager->parseIncludes($include);
// make resource
$resource = new Item($user, new UserTransformer($this->parameters), 'users');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
}

View File

@@ -334,7 +334,7 @@ class TransactionRequest extends Request
}
/**
* Throws an error when the given opping account (of type $type) is invalid.
* Throws an error when the given opposing account (of type $type) is invalid.
* Empty data is allowed, system will default to cash.
*
* @param Validator $validator
@@ -342,32 +342,32 @@ class TransactionRequest extends Request
* @param int|null $accountId
* @param null|string $accountName
* @param string $idField
* @param string $nameField
*/
protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField, string $nameField
): void {
protected function opposingAccountExists(Validator $validator, string $type, ?int $accountId, ?string $accountName, string $idField): void {
$accountId = intval($accountId);
$accountName = strval($accountName);
// both empty? done!
if ($accountId < 1 && strlen($accountName) === 0) {
return;
}
// ID belongs to user and is $type account:
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$set = $repository->getAccountsById([$accountId]);
if ($set->count() === 1) {
/** @var Account $first */
$first = $set->first();
if ($first->accountType->type !== $type) {
$validator->errors()->add($idField, trans('validation.belongs_user'));
if ($accountId !== 0) {
// ID belongs to user and is $type account:
/** @var AccountRepositoryInterface $repository */
$repository = app(AccountRepositoryInterface::class);
$repository->setUser(auth()->user());
$set = $repository->getAccountsById([$accountId]);
if ($set->count() === 1) {
/** @var Account $first */
$first = $set->first();
if ($first->accountType->type !== $type) {
$validator->errors()->add($idField, trans('validation.belongs_user'));
return;
}
// we ignore the account name at this point.
return;
}
// we ignore the account name at this point.
return;
}
// not having an opposing account by this name is NOT a problem.
@@ -390,7 +390,7 @@ class TransactionRequest extends Request
/** @var Transaction $transaction */
$transaction = $this->route()->parameter('transaction');
if (is_null($transaction)) {
return;
return; // @codeCoverageIgnore
}
$data['type'] = strtolower($transaction->transactionJournal->transactionType->type);
}
@@ -407,13 +407,11 @@ class TransactionRequest extends Request
$this->assetAccountExists($validator, $sourceId, $sourceName, $idField, $nameField);
$idField = 'transactions.' . $index . '.destination_id';
$nameField = 'transactions.' . $index . '.destination_name';
$this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField, $nameField);
$this->opposingAccountExists($validator, AccountType::EXPENSE, $destinationId, $destinationName, $idField);
break;
case 'deposit':
$idField = 'transactions.' . $index . '.source_id';
$nameField = 'transactions.' . $index . '.source_name';
$this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField, $nameField);
$this->opposingAccountExists($validator, AccountType::REVENUE, $sourceId, $sourceName, $idField);
$idField = 'transactions.' . $index . '.destination_id';
$nameField = 'transactions.' . $index . '.destination_name';
@@ -447,7 +445,8 @@ class TransactionRequest extends Request
if ($count < 2) {
return;
}
// this is pretty much impossible:
// @codeCoverageIgnoreStart
if (!isset($data['type'])) {
// the journal may exist in the request:
/** @var Transaction $transaction */
@@ -457,6 +456,7 @@ class TransactionRequest extends Request
}
$data['type'] = strtolower($transaction->transactionJournal->transactionType->type);
}
// @codeCoverageIgnoreEnd
// collect all source ID's and destination ID's, if present:
$sources = [];
@@ -487,7 +487,11 @@ class TransactionRequest extends Request
}
break;
default:
throw new FireflyException(sprintf('The validator cannot handle transaction type "%s" in validateSplitAccounts().', $data['type']));
// @codeCoverageIgnoreStart
throw new FireflyException(
sprintf('The validator cannot handle transaction type "%s" in validateSplitAccounts().', $data['type'])
);
// @codeCoverageIgnoreEnd
}
return;

View File

@@ -0,0 +1,89 @@
<?php
/**
* UserRequest.php
* Copyright (c) 2018 thegrumpydictator@gmail.com
*
* This file is part of Firefly III.
*
* Firefly III is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Firefly III is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Requests;
use FireflyIII\User;
/**
* Class UserRequest
*/
class UserRequest extends Request
{
/**
* @return bool
*/
public function authorize(): bool
{
// Only allow authenticated users
if (!auth()->check()) {
return false;
}
/** @var User $user */
$user = auth()->user();
if (!$user->hasRole('owner')) {
return false;
}
return true;
}
/**
* @return array
*/
public function getAll(): array
{
$data = [
'email' => $this->string('email'),
'blocked' => $this->boolean('blocked'),
'blocked_code' => $this->string('blocked_code'),
];
return $data;
}
/**
* @return array
*/
public function rules(): array
{
$rules = [
'email' => 'required|email|unique:users,email,',
'blocked' => 'required|boolean',
'blocked_code' => 'in:email_changed',
];
switch ($this->method()) {
default:
break;
case 'PUT':
case 'PATCH':
$user = $this->route()->parameter('user');
$rules['email'] = 'required|email|unique:users,email,' . $user->id;
break;
}
return $rules;
}
}