Fake jobs can be configured and can reach the landing stage.

This commit is contained in:
James Cole 2018-04-29 20:01:03 +02:00
parent fa41d6df04
commit f027d71136
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
13 changed files with 1464 additions and 1180 deletions

View File

@ -87,6 +87,8 @@ class IndexController extends Controller
// redirect to global prerequisites
return redirect(route('import.prerequisites.index', [$importProvider, $importJob->key]));
}
// update job to say "has_prereq".
$this->repository->setStatus($importJob, 'has_prereq');
// Otherwise just redirect to job configuration.
return redirect(route('import.job.configuration.index', [$importJob->key]));

View File

@ -25,7 +25,7 @@ namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Configuration\ConfiguratorInterface;
use FireflyIII\Import\JobConfiguration\JobConfiguratorInterface;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\Request;
@ -73,10 +73,10 @@ class JobConfigurationController extends Controller
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
$this->repository->updateStatus($job, 'configured');
if ($configurator->configurationComplete()) {
$this->repository->updateStatus($job, 'ready_to_run');
return redirect(route('import.status', [$job->key]));
return redirect(route('import.job.landing', [$job->key]));
}
$this->repository->updateStatus($job, 'configuring');
@ -105,40 +105,39 @@ class JobConfigurationController extends Controller
$configurator = $this->makeConfigurator($job);
// is the job already configured?
if ($configurator->isJobConfigured()) {
return redirect(route('import.status', [$job->key]));
if ($configurator->configurationComplete()) {
$this->repository->updateStatus($job, 'ready_to_run');
return redirect(route('import.job.landing', [$job->key]));
}
$data = $request->all();
$configurator->configureJob($data);
$messages = $configurator->configureJob($data);
// get possible warning from configurator:
$warning = $configurator->getWarningMessage();
if (\strlen($warning) > 0) {
$request->session()->flash('warning', $warning);
if ($messages->count() > 0) {
$request->session()->flash('warning', $messages->first());
}
// return to configure
return redirect(route('import.configure', [$job->key]));
return redirect(route('import.job.configuration.index', [$job->key]));
}
/**
* @param ImportJob $job
*
* @return ConfiguratorInterface
* @return JobConfiguratorInterface
*
* @throws FireflyException
*/
private function makeConfigurator(ImportJob $job): ConfiguratorInterface
private function makeConfigurator(ImportJob $job): JobConfiguratorInterface
{
$type = $job->file_type;
$key = sprintf('import.configuration.%s', $type);
$className = config($key);
$key = sprintf('import.configuration.%s', $job->provider);
$className = (string)config($key);
if (null === $className || !class_exists($className)) {
throw new FireflyException(sprintf('Cannot find configurator class for job of type "%s".', $type)); // @codeCoverageIgnore
throw new FireflyException(sprintf('Cannot find configurator class for job with provider "%s".', $job->provider)); // @codeCoverageIgnore
}
Log::debug(sprintf('Going to create class "%s"', $className));
/** @var ConfiguratorInterface $configurator */
/** @var JobConfiguratorInterface $configurator */
$configurator = app($className);
$configurator->setJob($job);

View File

@ -145,6 +145,9 @@ class PrerequisitesController extends Controller
return redirect(route('import.index'));
}
// update job:
$this->repository->setStatus($importJob, 'has_prereq');
// redirect to job config:
return redirect(route('import.job.configuration.index', [$importJob->key]));

View File

@ -0,0 +1,133 @@
<?php
/**
* FakeJobConfiguration.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\Import\JobConfiguration;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Support\MessageBag;
/**
* Class FakeJobConfiguration
*/
class FakeJobConfiguration implements JobConfiguratorInterface
{
/** @var ImportJob */
private $job;
/** @var ImportJobRepositoryInterface */
private $repository;
/**
* ConfiguratorInterface constructor.
*/
public function __construct()
{
$this->repository = app(ImportJobRepositoryInterface::class);
}
/**
* Returns true when the initial configuration for this job is complete.
*
* @return bool
*/
public function configurationComplete(): bool
{
// configuration array of job must have two values:
// 'artist' must be 'david bowie', case insensitive
// 'song' must be 'golden years', case insensitive.
$config = $this->job->configuration;
return (isset($config['artist']) && 'david bowie' === strtolower($config['artist']))
&& (isset($config['song']) && 'golden years' === strtolower($config['song']));
}
/**
* Store any data from the $data array into the job.
*
* @param array $data
*
* @return MessageBag
*/
public function configureJob(array $data): MessageBag
{
$artist = strtolower($data['artist'] ?? '');
$configuration = $this->job->configuration;
if ($artist === 'david bowie') {
// store artist
$configuration['artist'] = $artist;
}
$song = strtolower($data['song'] ?? '');
if ($song === 'golden years') {
// store artist
$configuration['song'] = $song;
}
$this->repository->setConfiguration($this->job, $configuration);
$messages = new MessageBag();
if (\count($configuration) !== 2) {
$messages->add('some_key', 'Ignore this error');
}
return $messages;
}
/**
* Return the data required for the next step in the job configuration.
*
* @return array
*/
public function getNextData(): array
{
return [];
}
/**
* Returns the view of the next step in the job configuration.
*
* @return string
*/
public function getNextView(): string
{
// first configure artist:
$config = $this->job->configuration;
$artist = $config['artist'] ?? '';
$song = $config['song'] ?? '';
if (strtolower($artist) !== 'david bowie') {
return 'import.fake.enter-artist';
}
if (strtolower($song) !== 'golden years') {
return 'import.fake.enter-song';
}
}
/**
* @param ImportJob $job
*/
public function setJob(ImportJob $job): void
{
$this->job = $job;
$this->repository->setUser($job->user);
}
}

View File

@ -0,0 +1,73 @@
<?php
/**
* JobConfiguratorInterface.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\Import\JobConfiguration;
use FireflyIII\Models\ImportJob;
use Illuminate\Support\MessageBag;
/**
* Interface JobConfiguratorInterface.
*/
interface JobConfiguratorInterface
{
/**
* ConfiguratorInterface constructor.
*/
public function __construct();
/**
* Store any data from the $data array into the job. Anything in the message bag will be flashed
* as an error to the user, regardless of its content.
*
* @param array $data
*
* @return MessageBag
*/
public function configureJob(array $data): MessageBag;
/**
* Return the data required for the next step in the job configuration.
*
* @return array
*/
public function getNextData(): array;
/**
* Returns the view of the next step in the job configuration.
*
* @return string
*/
public function getNextView(): string;
/**
* Returns true when the initial configuration for this job is complete.
*
* @return bool
*/
public function configurationComplete(): bool;
/**
* @param ImportJob $job
*/
public function setJob(ImportJob $job): void;
}

View File

@ -22,12 +22,9 @@ declare(strict_types=1);
namespace FireflyIII\Models;
use Crypt;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Log;
use Storage;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
@ -37,13 +34,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
*/
class ImportJob extends Model
{
/**
* @var array
*/
public $validStatus
= [
'new',
];
/**
* The attributes that should be casted to native types.
*
@ -58,7 +49,7 @@ class ImportJob extends Model
'transactions' => 'array',
];
/** @var array */
protected $fillable = ['key', 'user_id', 'file_type', 'source', 'status', 'stage', 'configuration', 'extended_status', 'transactions'];
protected $fillable = ['key', 'user_id', 'file_type', 'provider', 'status', 'stage', 'configuration', 'extended_status', 'transactions'];
/**
* @param $value
@ -74,49 +65,12 @@ class ImportJob extends Model
$key = trim($value);
$importJob = auth()->user()->importJobs()->where('key', $key)->first();
if (null !== $importJob) {
// must have valid status:
if (!\in_array($importJob->status, $importJob->validStatus)) {
throw new FireflyException(sprintf('ImportJob with key "%s" has invalid status "%s"', $importJob->key, $importJob->status));
}
return $importJob;
}
}
throw new NotFoundHttpException;
}
/**
* @deprecated
*
* @param int $count
*/
public function addTotalSteps(int $count): void
{
$status = $this->extended_status;
$status['steps'] += $count;
$this->extended_status = $status;
$this->save();
Log::debug(sprintf('Add %d to total steps for job "%s" making total steps %d', $count, $this->key, $status['steps']));
}
/**
* @return string
* @deprecated
* @throws \Illuminate\Contracts\Encryption\DecryptException
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function uploadFileContents(): string
{
$fileName = $this->key . '.upload';
$disk = Storage::disk('upload');
$encryptedContent = $disk->get($fileName);
$content = Crypt::decrypt($encryptedContent);
$content = trim($content);
Log::debug(sprintf('Content size is %d bytes.', \strlen($content)));
return $content;
}
/**
* @codeCoverageIgnore
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo

View File

@ -126,7 +126,7 @@ class ImportJobRepository implements ImportJobRepositoryInterface
$importJob = ImportJob::create(
[
'user_id' => $this->user->id,
'source' => $importProvider,
'provider' => $importProvider,
'file_type' => '',
'key' => Str::random(12),
'status' => 'new',

View File

@ -5,6 +5,7 @@ use FireflyIII\Import\Configuration\BunqConfigurator;
use FireflyIII\Import\Configuration\FileConfigurator;
use FireflyIII\Import\Configuration\SpectreConfigurator;
use FireflyIII\Import\FileProcessor\CsvProcessor;
use FireflyIII\Import\JobConfiguration\FakeJobConfiguration;
use FireflyIII\Import\Prerequisites\BunqPrerequisites;
use FireflyIII\Import\Prerequisites\FakePrerequisites;
use FireflyIII\Import\Prerequisites\FilePrerequisites;
@ -72,6 +73,7 @@ return [
'yodlee' => true,
],
'configuration' => [
'fake' => FakeJobConfiguration::class,
'file' => FileConfigurator::class,
'bunq' => BunqConfigurator::class,
'spectre' => SpectreConfigurator::class,

View File

@ -167,6 +167,7 @@ return [
'prerequisites_breadcrumb_plaid' => 'Prerequisites for Plaid',
'prerequisites_breadcrumb_quovo' => 'Prerequisites for Quovo',
'prerequisites_breadcrumb_yodlee' => 'Prerequisites for Yodlee',
// success messages:
'prerequisites_saved_for_fake' => 'API key stored for fake provider',
'prerequisites_saved_for_file' => 'Data stored for file imports',
@ -215,9 +216,11 @@ return [
'do_config_yodlee' => 'Configuration for imports from Yodlee',
'do_config_quovo' => 'Configuration for imports from Quovo',
// job configuration:
'job_configuration_breadcrumb' => 'Configuration for job ":key"',
// import index page:
'index_breadcrumb' => 'Index',
'upload_error' => 'The file you have uploaded could not be processed. Possibly it is of an invalid file type or encoding. The log files will have more information.',

View File

@ -0,0 +1,53 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.render }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Enter artist for fake import</h3>
</div>
<div class="box-body">
<p>
Enter "David Bowie", no matter the capitalization.
</p>
</div>
</div>
</div>
</div>
<form method="POST" action="{{ route('import.job.configuration.post', job.key) }}" accept-charset="UTF-8" class="form-horizontal" enctype="multipart/form-data">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Fields be here.</h3>
</div>
<div class="box-body">
{{ ExpandedForm.text('artist') }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-body">
<button type="submit" class="btn btn-success pull-right">
Submit it!
</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
{% block scripts %}
{% endblock %}
{% block styles %}
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.render }}
{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Enter song for fake import</h3>
</div>
<div class="box-body">
<p>
Enter "golden years", no matter the capitalization.
</p>
</div>
</div>
</div>
</div>
<form method="POST" action="{{ route('import.job.configuration.post', job.key) }}" accept-charset="UTF-8" class="form-horizontal" enctype="multipart/form-data">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">Fields be here.</h3>
</div>
<div class="box-body">
{{ ExpandedForm.text('song') }}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-12">
<div class="box">
<div class="box-body">
<button type="submit" class="btn btn-success pull-right">
Submit it!
</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
{% block scripts %}
{% endblock %}
{% block styles %}
{% endblock %}

View File

@ -22,6 +22,7 @@ declare(strict_types=1);
use Carbon\Carbon;
use DaveJamesMiller\Breadcrumbs\BreadcrumbsGenerator;
use DaveJamesMiller\Breadcrumbs\Exceptions\DuplicateBreadcrumbException;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Account;
use FireflyIII\Models\Attachment;
@ -42,6 +43,7 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Support\Collection;
try {
// HOME
Breadcrumbs::register(
'home',
@ -585,10 +587,10 @@ Breadcrumbs::register(
);
Breadcrumbs::register(
'import.configure',
'import.job.configuration.index',
function (BreadCrumbsGenerator $breadcrumbs, ImportJob $job) {
$breadcrumbs->parent('import.index');
$breadcrumbs->push(trans('import.config_sub_title', ['key' => $job->key]), route('import.configure', [$job->key]));
$breadcrumbs->push(trans('import.job_configuration_breadcrumb', ['key' => $job->key]), route('import.job.configuration.index', [$job->key]));
}
);
@ -1030,3 +1032,5 @@ Breadcrumbs::register(
$breadcrumbs->push(trans('breadcrumbs.edit_journal', ['description' => $journal->description]), route('transactions.split.edit', [$journal->id]));
}
);
} catch (DuplicateBreadcrumbException $e) {
}

View File

@ -451,6 +451,11 @@ Route::group(
// set global prerequisites for an import source, possible with a job already attached.
Route::get('prerequisites/{import_provider}/{importJob?}', ['uses' => 'Import\PrerequisitesController@index', 'as' => 'prerequisites.index']);
Route::post('prerequisites/{import_provider}/{importJob?}', ['uses' => 'Import\PrerequisitesController@post', 'as' => 'prerequisites.post']);
// configure a job:
Route::get('job/configuration/{importJob}', ['uses' => 'Import\JobConfigurationController@index', 'as' => 'job.configuration.index']);
Route::post('job/configuration/{importJob}', ['uses' => 'Import\JobConfigurationController@post', 'as' => 'job.configuration.post']);
// import method prerequisites:
#
#
@ -460,7 +465,7 @@ Route::group(
#Route::get('create/{bank}', ['uses' => 'Import\IndexController@create', 'as' => 'create-job']);
// configure the job:
#Route::get('configure/{importJob}', ['uses' => 'Import\ConfigurationController@index', 'as' => 'configure']);
#Route::post('configure/{importJob}', ['uses' => 'Import\ConfigurationController@post', 'as' => 'configure.post']);
// get status of any job: