From 67fa4a0fc7d81c3655133a91758ae37465f50929 Mon Sep 17 00:00:00 2001 From: James Cole Date: Fri, 13 Apr 2018 17:28:11 +0200 Subject: [PATCH] Add currency controls to API --- app/Api/V1/Controllers/CurrencyController.php | 214 ++++++++++++++++++ app/Api/V1/Controllers/UserController.php | 7 +- app/Api/V1/Requests/CurrencyRequest.php | 86 +++++++ app/Factory/TransactionJournalFactory.php | 2 +- app/Http/Requests/Request.php | 7 + app/Transformers/BillTransformer.php | 2 + app/Transformers/CurrencyTransformer.php | 96 ++++++++ routes/api.php | 12 + 8 files changed, 422 insertions(+), 4 deletions(-) create mode 100644 app/Api/V1/Controllers/CurrencyController.php create mode 100644 app/Api/V1/Requests/CurrencyRequest.php create mode 100644 app/Transformers/CurrencyTransformer.php diff --git a/app/Api/V1/Controllers/CurrencyController.php b/app/Api/V1/Controllers/CurrencyController.php new file mode 100644 index 0000000000..2302f3b4be --- /dev/null +++ b/app/Api/V1/Controllers/CurrencyController.php @@ -0,0 +1,214 @@ +. + */ + +declare(strict_types=1); +/** + * CurrencyController.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 . + */ + +namespace FireflyIII\Api\V1\Controllers; + +use FireflyIII\Api\V1\Requests\BillRequest; +use FireflyIII\Api\V1\Requests\CurrencyRequest; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\TransactionCurrency; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\User\UserRepositoryInterface; +use FireflyIII\Transformers\CurrencyTransformer; +use Illuminate\Http\Request; +use League\Fractal\Manager; +use League\Fractal\Resource\Collection as FractalCollection; +use League\Fractal\Resource\Item; +use League\Fractal\Serializer\JsonApiSerializer; +use Preferences; + +/** + * Class CurrencyController + */ +class CurrencyController extends Controller +{ + /** @var CurrencyRepositoryInterface */ + private $repository; + /** @var UserRepositoryInterface */ + private $userRepository; + + /** + * CurrencyRepository constructor. + * + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function __construct() + { + parent::__construct(); + $this->middleware( + function ($request, $next) { + /** @var CurrencyRepositoryInterface repository */ + $this->repository = app(CurrencyRepositoryInterface::class); + $this->userRepository = app(UserRepositoryInterface::class); + $this->repository->setUser(auth()->user()); + + return $next($request); + } + ); + } + + /** + * Remove the specified resource from storage. + * + * @param TransactionCurrency $currency + * + * @return \Illuminate\Http\Response + * @throws FireflyException + */ + public function delete(TransactionCurrency $currency) + { + if (!$this->userRepository->hasRole(auth()->user(), 'owner')) { + // access denied: + throw new FireflyException('No access to method, user is not owner.'); // @codeCoverageIgnore + } + if (!$this->repository->canDeleteCurrency($currency)) { + throw new FireflyException('No access to method, currency is in use.'); // @codeCoverageIgnore + } + $this->repository->destroy($currency); + + return response()->json([], 204); + } + + /** + * Display a listing of the resource. + * + * @param Request $request + * + * @return \Illuminate\Http\JsonResponse + */ + public function index(Request $request) + { + $collection = $this->repository->get(); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + $resource = new FractalCollection($collection, new CurrencyTransformer($this->parameters), 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + + /** + * @param Request $request + * @param TransactionCurrency $currency + * + * @return \Illuminate\Http\JsonResponse + */ + public function show(Request $request, TransactionCurrency $currency) + { + $manager = new Manager(); + // add include parameter: + $include = $request->get('include') ?? ''; + $manager->parseIncludes($include); + + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + $resource = new Item($currency, new CurrencyTransformer($this->parameters), 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + + /** + * @param CurrencyRequest $request + * + * @return \Illuminate\Http\JsonResponse + * @throws FireflyException + */ + public function store(CurrencyRequest $request) + { + $currency = $this->repository->store($request->getAll()); + + if ($request->boolean('default') === true) { + Preferences::set('currencyPreference', $currency->code); + Preferences::mark(); + } + if (null !== $currency) { + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + $resource = new Item($currency, new CurrencyTransformer($this->parameters), 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + } + throw new FireflyException('Could not store new currency.'); + + } + + + /** + * @param BillRequest $request + * @param TransactionCurrency $currency + * + * @return \Illuminate\Http\JsonResponse + */ + public function update(CurrencyRequest $request, TransactionCurrency $currency) + { + $data = $request->getAll(); + $currency = $this->repository->update($currency, $data); + + if ($request->boolean('default') === true) { + Preferences::set('currencyPreference', $currency->code); + Preferences::mark(); + } + + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $defaultCurrency = app('amount')->getDefaultCurrencyByUser(auth()->user()); + $this->parameters->set('defaultCurrency', $defaultCurrency); + + $resource = new Item($currency, new CurrencyTransformer($this->parameters), 'currencies'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); + + } +} diff --git a/app/Api/V1/Controllers/UserController.php b/app/Api/V1/Controllers/UserController.php index 850f6fe3a3..1cf1bece04 100644 --- a/app/Api/V1/Controllers/UserController.php +++ b/app/Api/V1/Controllers/UserController.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace FireflyIII\Api\V1\Controllers; use FireflyIII\Api\V1\Requests\UserRequest; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Transformers\UserTransformer; use FireflyIII\User; @@ -35,7 +36,7 @@ use League\Fractal\Resource\Collection as FractalCollection; use League\Fractal\Resource\Item; use League\Fractal\Serializer\JsonApiSerializer; use Preferences; -use Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException; + /** * Class UserController @@ -70,7 +71,7 @@ class UserController extends Controller * @param \FireflyIII\User $user * * @return \Illuminate\Http\Response - * @throws \Symfony\Component\HttpFoundation\File\Exception\AccessDeniedException + * @throws FireflyException */ public function delete(User $user) { @@ -79,7 +80,7 @@ class UserController extends Controller return response()->json([], 204); } - throw new AccessDeniedException(''); // @codeCoverageIgnore + throw new FireflyException('No access to method.'); // @codeCoverageIgnore } /** diff --git a/app/Api/V1/Requests/CurrencyRequest.php b/app/Api/V1/Requests/CurrencyRequest.php new file mode 100644 index 0000000000..1a3c3c020c --- /dev/null +++ b/app/Api/V1/Requests/CurrencyRequest.php @@ -0,0 +1,86 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Api\V1\Requests; + + +/** + * Class CurrencyRequest + */ +class CurrencyRequest extends Request +{ + /** + * @return bool + */ + public function authorize(): bool + { + // Only allow authenticated users + return auth()->check(); + } + + /** + * @return array + */ + public function getAll() + { + return [ + 'name' => $this->string('name'), + 'code' => $this->string('code'), + 'symbol' => $this->string('symbol'), + 'decimal_places' => $this->integer('decimal_places'), + 'default' => $this->boolean('default'), + ]; + } + + /** + * @return array + */ + public function rules(): array + { + $rules = [ + 'name' => 'required|between:1,255|unique:transaction_currencies,name', + 'code' => 'required|between:3,3|unique:transaction_currencies,code', + 'symbol' => 'required|between:1,5|unique:transaction_currencies,symbol', + 'decimal_places' => 'required|between:0,20|numeric|min:0|max:20', + 'default' => 'in:true,false', + ]; + + switch ($this->method()) { + default: + break; + case 'PUT': + case 'PATCH': + $currency = $this->route()->parameter('currency'); + $rules['name'] = 'required|between:1,255|unique:transaction_currencies,name,' . $currency->id; + $rules['code'] = 'required|between:1,255|unique:transaction_currencies,code,' . $currency->id; + $rules['symbol'] = 'required|between:1,255|unique:transaction_currencies,symbol,' . $currency->id; + //$bill = $this->route()->parameter('bill'); + //$rules['name'] .= ',' . $bill->id; + //$rules['match'] .= ',' . $bill->id; + break; + } + + return $rules; + + } +} \ No newline at end of file diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 6d452d0a01..a4422b7b71 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -91,7 +91,7 @@ class TransactionJournalFactory // store date meta fields (if present): $fields = ['sepa-cc', 'sepa-ct-op', 'sepa-ct-id', 'sepa-db', 'sepa-country', 'sepa-ep', 'sepa-ci', 'interest_date', 'book_date', 'process_date', - 'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id']; + 'due_date', 'payment_date', 'invoice_date', 'internal_reference', 'bunq_payment_id','importHash']; foreach ($fields as $field) { $this->storeMeta($journal, $data, $field); diff --git a/app/Http/Requests/Request.php b/app/Http/Requests/Request.php index fb9162f74d..1146b31017 100644 --- a/app/Http/Requests/Request.php +++ b/app/Http/Requests/Request.php @@ -37,6 +37,13 @@ class Request extends FormRequest */ public function boolean(string $field): bool { + if ((string)$this->input($field) === 'true') { + return true; + } + if ((string)$this->input($field) === 'false') { + return false; + } + return 1 === (int)$this->input($field); } diff --git a/app/Transformers/BillTransformer.php b/app/Transformers/BillTransformer.php index 80572eca0b..78a17fe43d 100644 --- a/app/Transformers/BillTransformer.php +++ b/app/Transformers/BillTransformer.php @@ -139,6 +139,8 @@ class BillTransformer extends TransformerAbstract 'updated_at' => $bill->updated_at->toAtomString(), 'created_at' => $bill->created_at->toAtomString(), 'name' => $bill->name, + 'currency_id' => $bill->transaction_currency_id, + 'currency_code' => $bill->transactionCurrency->code, 'match' => explode(',', $bill->match), 'amount_min' => round($bill->amount_min, 2), 'amount_max' => round($bill->amount_max, 2), diff --git a/app/Transformers/CurrencyTransformer.php b/app/Transformers/CurrencyTransformer.php new file mode 100644 index 0000000000..e399d93dc6 --- /dev/null +++ b/app/Transformers/CurrencyTransformer.php @@ -0,0 +1,96 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Transformers; + +use FireflyIII\Models\TransactionCurrency; +use League\Fractal\TransformerAbstract; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class CurrencyTransformer + */ +class CurrencyTransformer extends TransformerAbstract +{ + /** + * List of resources possible to include + * + * @var array + */ + protected $availableIncludes = []; + /** + * List of resources to automatically include + * + * @var array + */ + protected $defaultIncludes = []; + + /** @var ParameterBag */ + protected $parameters; + + /** + * CurrencyTransformer constructor. + * + * @codeCoverageIgnore + * + * @param ParameterBag $parameters + */ + public function __construct(ParameterBag $parameters) + { + $this->parameters = $parameters; + } + + /** + * Transform the currency. + * + * @param TransactionCurrency $currency + * + * @return array + */ + public function transform(TransactionCurrency $currency): array + { + $isDefault = false; + $defaultCurrency = $this->parameters->get('defaultCurrency'); + if (null !== $defaultCurrency) { + $isDefault = $defaultCurrency->id === $currency->id; + } + $data = [ + 'id' => (int)$currency->id, + 'updated_at' => $currency->updated_at->toAtomString(), + 'created_at' => $currency->created_at->toAtomString(), + 'name' => $currency->name, + 'code' => $currency->code, + 'symbol' => $currency->symbol, + 'decimal_places' => (int)$currency->decimal_places, + 'default' => $isDefault, + 'links' => [ + [ + 'rel' => 'self', + 'uri' => '/currencies/' . $currency->id, + ], + ], + ]; + + return $data; + } +} diff --git a/routes/api.php b/routes/api.php index 53e6fb1ab5..eeca33c0f6 100644 --- a/routes/api.php +++ b/routes/api.php @@ -57,6 +57,18 @@ Route::group( ); +Route::group( + ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'currencies', 'as' => 'api.v1.currencies.'], function () { + + // Transaction currency API routes: + Route::get('', ['uses' => 'CurrencyController@index', 'as' => 'index']); + Route::post('', ['uses' => 'CurrencyController@store', 'as' => 'store']); + Route::get('{currency}', ['uses' => 'CurrencyController@show', 'as' => 'show']); + Route::put('{currency}', ['uses' => 'CurrencyController@update', 'as' => 'update']); + Route::delete('{currency}', ['uses' => 'CurrencyController@delete', 'as' => 'delete']); +} +); + Route::group( ['middleware' => ['auth:api', 'bindings'], 'namespace' => 'FireflyIII\Api\V1\Controllers', 'prefix' => 'transactions', 'as' => 'api.v1.transactions.'], function () {