mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-18 18:44:16 +00:00
Implement user API and first tests.
This commit is contained in:
@@ -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';
|
||||
|
@@ -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');
|
||||
|
||||
}
|
||||
|
||||
|
@@ -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;
|
||||
|
89
app/Api/V1/Requests/UserRequest.php
Normal file
89
app/Api/V1/Requests/UserRequest.php
Normal 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;
|
||||
}
|
||||
|
||||
}
|
@@ -95,8 +95,9 @@ class Sandstorm
|
||||
/** @var User $user */
|
||||
$user = $repository->store(
|
||||
[
|
||||
'blocked' => false,
|
||||
'blocked_code' => null,
|
||||
'email' => $email,
|
||||
'password' => str_random(16),
|
||||
]
|
||||
);
|
||||
Auth::guard($guard)->login($user);
|
||||
|
@@ -264,12 +264,12 @@ class UserRepository implements UserRepositoryInterface
|
||||
*/
|
||||
public function store(array $data): User
|
||||
{
|
||||
$password = bcrypt($data['password'] ?? app('str')->random(16));
|
||||
|
||||
return User::create(
|
||||
[
|
||||
'email' => $data['email'],
|
||||
'password' => $password,
|
||||
'blocked' => $data['blocked'] ?? false,
|
||||
'blocked_code' => $data['blocked_code'] ?? null,
|
||||
'email' => $data['email'],
|
||||
'password' => str_random(24),
|
||||
]
|
||||
);
|
||||
}
|
||||
@@ -286,6 +286,24 @@ class UserRepository implements UserRepositoryInterface
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user info.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function update(User $user, array $data): User
|
||||
{
|
||||
$this->updateEmail($user, $data['email']);
|
||||
$user->blocked = $data['blocked'] ?? false;
|
||||
$user->blocked_code = $data['blocked_code'] ?? null;
|
||||
$user->save();
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* This updates the users email address. Same as changeEmail just without most logging. This makes sure that the undo/confirm routine can't catch this one.
|
||||
* The user is NOT blocked.
|
||||
|
@@ -31,7 +31,6 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
interface UserRepositoryInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a collection of all users.
|
||||
*
|
||||
@@ -159,6 +158,16 @@ interface UserRepositoryInterface
|
||||
*/
|
||||
public function unblockUser(User $user): void;
|
||||
|
||||
/**
|
||||
* Update user info.
|
||||
*
|
||||
* @param User $user
|
||||
* @param array $data
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function update(User $user, array $data): User;
|
||||
|
||||
/**
|
||||
* This updates the users email address. Same as changeEmail just without most logging. This makes sure that the undo/confirm routine can't catch this one.
|
||||
* The user is NOT blocked.
|
||||
|
@@ -26,6 +26,7 @@ namespace FireflyIII\Transformers;
|
||||
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\User;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\TransformerAbstract;
|
||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
|
||||
@@ -34,6 +35,19 @@ use Symfony\Component\HttpFoundation\ParameterBag;
|
||||
*/
|
||||
class UserTransformer extends TransformerAbstract
|
||||
{
|
||||
/**
|
||||
* List of resources possible to include.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $availableIncludes = ['accounts', 'attachments', 'bills', 'budgets', 'categories', 'piggy_banks', 'tags', 'transactions'];
|
||||
/**
|
||||
* List of resources to automatically include
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $defaultIncludes = [];
|
||||
|
||||
/** @var ParameterBag */
|
||||
protected $parameters;
|
||||
|
||||
@@ -49,6 +63,116 @@ class UserTransformer extends TransformerAbstract
|
||||
$this->parameters = $parameters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Include accounts.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeAccounts(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->accounts, new AccountTransformer($this->parameters), 'accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
* Include attachments.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeAttachments(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->attachments, new AttachmentTransformer($this->parameters), 'attachments');
|
||||
}
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeBills(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->bills, new BillTransformer($this->parameters), 'bills');
|
||||
}
|
||||
|
||||
/**
|
||||
* Include budgets.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeBudgets(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->budgets, new BudgetTransformer($this->parameters), 'budgets');
|
||||
}
|
||||
|
||||
/**
|
||||
* Include categories.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeCategories(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->categories, new CategoryTransformer($this->parameters), 'categories');
|
||||
}
|
||||
|
||||
/**
|
||||
* Include piggy banks.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includePiggyBanks(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->piggyBanks, new PiggyBankTransformer($this->parameters), 'piggy_banks');
|
||||
}
|
||||
|
||||
/**
|
||||
* Include tags.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeTags(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->tags, new TagTransformer($this->parameters), 'tags');
|
||||
}
|
||||
|
||||
/**
|
||||
* Include transactions.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return FractalCollection
|
||||
*/
|
||||
public function includeTransactions(User $user): FractalCollection
|
||||
{
|
||||
return $this->collection($user->transactions, new TransactionTransformer($this->parameters), 'transactions');
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform user.
|
||||
*
|
||||
|
13
app/User.php
13
app/User.php
@@ -42,13 +42,23 @@ class User extends Authenticatable
|
||||
{
|
||||
use Notifiable, HasApiTokens;
|
||||
|
||||
/**
|
||||
* The attributes that should be casted to native types.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $casts
|
||||
= [
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
'blocked' => 'boolean',
|
||||
];
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = ['email', 'password', 'blocked', 'blocked_code'];
|
||||
|
||||
/**
|
||||
* The attributes excluded from the model's JSON form.
|
||||
*
|
||||
@@ -63,7 +73,6 @@ class User extends Authenticatable
|
||||
protected $table = 'users';
|
||||
|
||||
/**
|
||||
* @param $guard
|
||||
* @param string $value
|
||||
*
|
||||
* @return User
|
||||
|
@@ -61,7 +61,7 @@ Route::group(
|
||||
['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transactions', 'as' => 'api.v1.transactions.'],
|
||||
function () {
|
||||
|
||||
// Users API routes:
|
||||
// Transaction API routes:
|
||||
Route::get('', ['uses' => 'TransactionController@index', 'as' => 'index']);
|
||||
Route::post('', ['uses' => 'TransactionController@store', 'as' => 'store']);
|
||||
Route::get('{transaction}', ['uses' => 'TransactionController@show', 'as' => 'show']);
|
||||
|
@@ -37,7 +37,6 @@ use Log;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* todo test fire of rules with parameter
|
||||
* Class TransactionControllerTest
|
||||
*/
|
||||
class TransactionControllerTest extends TestCase
|
||||
@@ -1526,6 +1525,46 @@ class TransactionControllerTest extends TestCase
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add opposing account by a new name.
|
||||
*
|
||||
* @covers \FireflyIII\Api\V1\Controllers\TransactionController::store
|
||||
* @covers \FireflyIII\Api\V1\Requests\TransactionRequest
|
||||
*/
|
||||
public function testSuccessNewStoreOpposingName()
|
||||
{
|
||||
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 1)->first();
|
||||
$account = $this->user()->accounts()->where('account_type_id', 3)->first();
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class)->makePartial();
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
|
||||
$journalRepos->shouldReceive('setUser')->once();
|
||||
$accountRepos->shouldReceive('setUser');
|
||||
$accountRepos->shouldReceive('getAccountsById')->andReturn(new Collection([$account]));
|
||||
$journalRepos->shouldReceive('store')->andReturn($journal)->once();
|
||||
|
||||
$data = [
|
||||
'description' => 'Some transaction #' . rand(1, 1000),
|
||||
'date' => '2018-01-01',
|
||||
'type' => 'withdrawal',
|
||||
'transactions' => [
|
||||
[
|
||||
'amount' => '10',
|
||||
'currency_id' => 1,
|
||||
'source_id' => $account->id,
|
||||
'destination_name' => 'New expense account #' . rand(1, 1000),
|
||||
],
|
||||
|
||||
|
||||
],
|
||||
];
|
||||
|
||||
// test API
|
||||
$response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']);
|
||||
$response->assertStatus(200);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit the minimum amount of data required to create a withdrawal.
|
||||
*
|
||||
@@ -1574,7 +1613,7 @@ class TransactionControllerTest extends TestCase
|
||||
public function testSuccessStoreBasic()
|
||||
{
|
||||
// default journal:
|
||||
$journal = $this->user()->transactionJournals()->first();
|
||||
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 1)->first();
|
||||
$account = $this->user()->accounts()->where('account_type_id', 3)->first();
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class)->makePartial();
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
@@ -1613,7 +1652,7 @@ class TransactionControllerTest extends TestCase
|
||||
public function testSuccessStoreBasicDeposit()
|
||||
{
|
||||
// default journal:
|
||||
$journal = $this->user()->transactionJournals()->first();
|
||||
$journal = $this->user()->transactionJournals()->where('transaction_type_id', 2)->first();
|
||||
$account = $this->user()->accounts()->where('account_type_id', 3)->first();
|
||||
$journalRepos = $this->mock(JournalRepositoryInterface::class)->makePartial();
|
||||
$accountRepos = $this->mock(AccountRepositoryInterface::class);
|
||||
@@ -2330,4 +2369,78 @@ class TransactionControllerTest extends TestCase
|
||||
$response = $this->post('/api/v1/transactions?include=tags', $data, ['Accept' => 'application/json']);
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire enough to trigger an update. Since the create code already fires on the Request, no
|
||||
* need to verify all of that.
|
||||
*
|
||||
* @covers \FireflyIII\Api\V1\Controllers\TransactionController::update
|
||||
* @covers \FireflyIII\Api\V1\Requests\TransactionRequest
|
||||
*/
|
||||
public function testUpdateBasicDeposit()
|
||||
{
|
||||
$account = $this->user()->accounts()->where('account_type_id', 3)->first();
|
||||
$repository = $this->mock(JournalRepositoryInterface::class);
|
||||
$data = [
|
||||
'description' => 'Some deposit #' . rand(1, 1000),
|
||||
'date' => '2018-01-01',
|
||||
'transactions' => [
|
||||
[
|
||||
'amount' => '10',
|
||||
'currency_id' => 1,
|
||||
'destination_id' => $account->id,
|
||||
],
|
||||
],
|
||||
];
|
||||
do {
|
||||
/** @var TransactionJournal $deposit */
|
||||
$deposit = $this->user()->transactionJournals()->where('transaction_type_id', 2)->first();
|
||||
$count = $deposit->transactions()->count();
|
||||
} while ($count !== 2);
|
||||
|
||||
$transaction = $deposit->transactions()->first();
|
||||
$repository->shouldReceive('setUser');
|
||||
$repository->shouldReceive('update')->andReturn($deposit)->once();
|
||||
|
||||
// call API
|
||||
$response = $this->put('/api/v1/transactions/' . $transaction->id, $data);
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire enough to trigger an update. Since the create code already fires on the Request, no
|
||||
* need to verify all of that.
|
||||
*
|
||||
* @covers \FireflyIII\Api\V1\Controllers\TransactionController::update
|
||||
* @covers \FireflyIII\Api\V1\Requests\TransactionRequest
|
||||
*/
|
||||
public function testUpdateBasicWithdrawal()
|
||||
{
|
||||
$account = $this->user()->accounts()->where('account_type_id', 3)->first();
|
||||
$repository = $this->mock(JournalRepositoryInterface::class);
|
||||
$data = [
|
||||
'description' => 'Some transaction #' . rand(1, 1000),
|
||||
'date' => '2018-01-01',
|
||||
'transactions' => [
|
||||
[
|
||||
'amount' => '10',
|
||||
'currency_id' => 1,
|
||||
'source_id' => $account->id,
|
||||
],
|
||||
],
|
||||
];
|
||||
do {
|
||||
/** @var TransactionJournal $withdrawal */
|
||||
$withdrawal = $this->user()->transactionJournals()->where('transaction_type_id', 1)->first();
|
||||
$count = $withdrawal->transactions()->count();
|
||||
} while ($count !== 2);
|
||||
|
||||
$transaction = $withdrawal->transactions()->first();
|
||||
$repository->shouldReceive('setUser');
|
||||
$repository->shouldReceive('update')->andReturn($withdrawal)->once();
|
||||
|
||||
// call API
|
||||
$response = $this->put('/api/v1/transactions/' . $transaction->id, $data);
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
}
|
35
tests/Api/V1/Controllers/UserControllerTest.php
Normal file
35
tests/Api/V1/Controllers/UserControllerTest.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* UserControllerTest.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 Tests\Api\V1\Controllers;
|
||||
|
||||
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* Class UserControllerTest
|
||||
*/
|
||||
class UserControllerTest extends TestCase
|
||||
{
|
||||
|
||||
}
|
@@ -40,6 +40,9 @@ use Mockery;
|
||||
*/
|
||||
abstract class TestCase extends BaseTestCase
|
||||
{
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
* @param string $range
|
||||
|
Reference in New Issue
Block a user