Improved code for new import + some tests.

This commit is contained in:
James Cole 2018-05-04 20:21:27 +02:00
parent b541f7b944
commit 1c0da454db
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
22 changed files with 2770 additions and 2281 deletions

View File

@ -24,7 +24,6 @@ namespace FireflyIII\Http\Controllers\Import;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Import\Prerequisites\PrerequisitesInterface; use FireflyIII\Import\Prerequisites\PrerequisitesInterface;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use View; use View;
@ -54,7 +53,6 @@ class IndexController extends Controller
return $next($request); return $next($request);
} }
); );
$this->middleware(IsDemoUser::class)->except(['index']);
} }
/** /**
@ -68,11 +66,19 @@ class IndexController extends Controller
*/ */
public function create(string $importProvider) public function create(string $importProvider)
{ {
if (
!(bool)config('app.debug')
&& !(bool)config(sprintf('import.enabled.%s', $importProvider)) === true
&& !\in_array(config('app.env'), ['demo', 'testing'])
) {
throw new FireflyException(sprintf('Import using provider "%s" is currently not available.', $importProvider)); // @codeCoverageIgnore
}
$importJob = $this->repository->create($importProvider); $importJob = $this->repository->create($importProvider);
// if job provider has no prerequisites: // if job provider has no prerequisites:
if (!(bool)config(sprintf('import.has_prereq.%s', $importProvider))) { if (!(bool)config(sprintf('import.has_prereq.%s', $importProvider))) {
// @codeCoverageIgnoreStart
// if job provider also has no configuration: // if job provider also has no configuration:
if (!(bool)config(sprintf('import.has_config.%s', $importProvider))) { if (!(bool)config(sprintf('import.has_config.%s', $importProvider))) {
$this->repository->updateStatus($importJob, 'ready_to_run'); $this->repository->updateStatus($importJob, 'ready_to_run');
@ -85,6 +91,7 @@ class IndexController extends Controller
// redirect to job configuration. // redirect to job configuration.
return redirect(route('import.job.configuration.index', [$importJob->key])); return redirect(route('import.job.configuration.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
} }
// if need to set prerequisites, do that first. // if need to set prerequisites, do that first.
@ -122,7 +129,7 @@ class IndexController extends Controller
$config = config('import.enabled'); $config = config('import.enabled');
$providers = []; $providers = [];
foreach ($config as $name => $enabled) { foreach ($config as $name => $enabled) {
if ($enabled || (bool)config('app.debug')) { if ($enabled || (bool)config('app.debug') || \in_array(config('app.env'), ['demo', 'testing'])) {
$providers[$name] = []; $providers[$name] = [];
} }
} }
@ -147,110 +154,4 @@ class IndexController extends Controller
return view('import.index', compact('subTitle', 'subTitleIcon', 'providers')); return view('import.index', compact('subTitle', 'subTitleIcon', 'providers'));
} }
//
// /**
// * @param Request $request
// * @param string $bank
// *
// * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
// */
// public function reset(Request $request, string $bank)
// {
// if ($bank === 'bunq') {
// // remove bunq related preferences.
// Preferences::delete('bunq_api_key');
// Preferences::delete('bunq_server_public_key');
// Preferences::delete('bunq_private_key');
// Preferences::delete('bunq_public_key');
// Preferences::delete('bunq_installation_token');
// Preferences::delete('bunq_installation_id');
// Preferences::delete('bunq_device_server_id');
// Preferences::delete('external_ip');
//
// }
//
// if ($bank === 'spectre') {
// // remove spectre related preferences:
// Preferences::delete('spectre_client_id');
// Preferences::delete('spectre_app_secret');
// Preferences::delete('spectre_service_secret');
// Preferences::delete('spectre_app_id');
// Preferences::delete('spectre_secret');
// Preferences::delete('spectre_private_key');
// Preferences::delete('spectre_public_key');
// Preferences::delete('spectre_customer');
// }
//
// Preferences::mark();
// $request->session()->flash('info', (string)trans('firefly.settings_reset_for_' . $bank));
//
// return redirect(route('import.index'));
//
// }
// /**
// * @param ImportJob $job
// *
// * @return \Illuminate\Http\JsonResponse
// *
// * @throws FireflyException
// */
// public function start(ImportJob $job)
// {
// $type = $job->file_type;
// $key = sprintf('import.routine.%s', $type);
// $className = config($key);
// if (null === $className || !class_exists($className)) {
// throw new FireflyException(sprintf('Cannot find import routine class for job of type "%s".', $type)); // @codeCoverageIgnore
// }
//
// /** @var RoutineInterface $routine */
// $routine = app($className);
// $routine->setJob($job);
// $result = $routine->run();
//
// if ($result) {
// return response()->json(['run' => 'ok']);
// }
//
// throw new FireflyException('Job did not complete successfully. Please review the log files.');
// }
// /**
// * Generate a JSON file of the job's configuration and send it to the user.
// *
// * @param ImportJob $job
// *
// * @return LaravelResponse
// */
// public function download(ImportJob $job)
// {
// Log::debug('Now in download()', ['job' => $job->key]);
// $config = $job->configuration;
//
// // This is CSV import specific:
// $config['column-roles-complete'] = false;
// $config['column-mapping-complete'] = false;
// $config['initial-config-complete'] = false;
// $config['has-file-upload'] = false;
// $config['delimiter'] = "\t" === $config['delimiter'] ? 'tab' : $config['delimiter'];
// unset($config['stage']);
//
// $result = json_encode($config, JSON_PRETTY_PRINT);
// $name = sprintf('"%s"', addcslashes('import-configuration-' . date('Y-m-d') . '.json', '"\\'));
//
// /** @var LaravelResponse $response */
// $response = response($result, 200);
// $response->header('Content-disposition', 'attachment; filename=' . $name)
// ->header('Content-Type', 'application/json')
// ->header('Content-Description', 'File Transfer')
// ->header('Connection', 'Keep-Alive')
// ->header('Expires', '0')
// ->header('Cache-Control', 'must-revalidate, post-check=0, pre-check=0')
// ->header('Pragma', 'public')
// ->header('Content-Length', \strlen($result));
//
// return $response;
// }
} }

View File

@ -70,10 +70,10 @@ class JobConfigurationController extends Controller
public function index(ImportJob $importJob) public function index(ImportJob $importJob)
{ {
// catch impossible status: // catch impossible status:
$allowed = ['has_prereq', 'need_job_config', 'has_config']; $allowed = ['has_prereq', 'need_job_config'];
if (null !== $importJob && !in_array($importJob->status, $allowed)) { if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error('Job is not new but wants to do prerequisites');
session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status])); session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status]));
return redirect(route('import.index')); return redirect(route('import.index'));
} }
@ -82,10 +82,12 @@ class JobConfigurationController extends Controller
// if provider has no config, just push it through: // if provider has no config, just push it through:
$importProvider = $importJob->provider; $importProvider = $importJob->provider;
if (!(bool)config(sprintf('import.has_config.%s', $importProvider))) { if (!(bool)config(sprintf('import.has_config.%s', $importProvider))) {
// @codeCoverageIgnoreStart
Log::debug('Job needs no config, is ready to run!'); Log::debug('Job needs no config, is ready to run!');
$this->repository->updateStatus($importJob ,'ready_to_run'); $this->repository->updateStatus($importJob, 'ready_to_run');
return redirect(route('import.job.status.index', [$importJob->key])); return redirect(route('import.job.status.index', [$importJob->key]));
// @codeCoverageIgnoreEnd
} }
// create configuration class: // create configuration class:
@ -120,10 +122,10 @@ class JobConfigurationController extends Controller
public function post(Request $request, ImportJob $importJob) public function post(Request $request, ImportJob $importJob)
{ {
// catch impossible status: // catch impossible status:
$allowed = ['has_prereq', 'need_job_config', 'has_config']; $allowed = ['has_prereq', 'need_job_config'];
if (null !== $importJob && !in_array($importJob->status, $allowed)) { if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error('Job is not new but wants to do prerequisites');
session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status])); session()->flash('error', trans('import.bad_job_status', ['status' => $importJob->status]));
return redirect(route('import.index')); return redirect(route('import.index'));
} }

View File

@ -32,7 +32,6 @@ use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Log; use Log;
use Symfony\Component\Debug\Exception\FatalThrowableError;
/** /**
* Class JobStatusController * Class JobStatusController
@ -68,19 +67,6 @@ class JobStatusController extends Controller
*/ */
public function index(ImportJob $importJob) public function index(ImportJob $importJob)
{ {
// jump away depending on job status:
if ($importJob->status === 'has_prereq') {
// TODO back to configuration.
}
if ($importJob->status === 'errored') {
// TODO to error screen
}
if ($importJob->status === 'finished') {
// TODO to finished screen.
}
$subTitleIcon = 'fa-gear'; $subTitleIcon = 'fa-gear';
$subTitle = trans('import.job_status_breadcrumb', ['key' => $importJob->key]); $subTitle = trans('import.job_status_breadcrumb', ['key' => $importJob->key]);
@ -94,42 +80,44 @@ class JobStatusController extends Controller
*/ */
public function json(ImportJob $importJob): JsonResponse public function json(ImportJob $importJob): JsonResponse
{ {
$extendedStatus = $importJob->extended_status; $count = \count($importJob->transactions);
$count = \count($importJob->transactions); $json = [
$json = [
'status' => $importJob->status, 'status' => $importJob->status,
'errors' => $importJob->errors, 'errors' => $importJob->errors,
'count' => $count, 'count' => $count,
'tag_id' => $importJob->tag_id, 'tag_id' => $importJob->tag_id,
'tag_name' => null === $importJob->tag_id ? null : $importJob->tag->tag, 'tag_name' => null === $importJob->tag_id ? null : $importJob->tag->tag,
'journals' => $extendedStatus['count'] ?? 0,
'report_txt' => trans('import.unknown_import_result'), 'report_txt' => trans('import.unknown_import_result'),
]; ];
// if count is zero: // if count is zero:
if (null !== $importJob->tag_id) {
$count = $importJob->tag->transactionJournals->count();
}
if ($count === 0) { if ($count === 0) {
$json['report_txt'] = trans('import.result_no_transactions'); $json['report_txt'] = trans('import.result_no_transactions');
} }
if ($count === 1 && null !== $importJob->tag_id) { if ($count === 1) {
$json['report_txt'] = trans('import.result_one_transaction', ['route' => route('tags.show', [$importJob->tag_id]), 'tag' => $importJob->tag->tag ]); $json['report_txt'] = trans('import.result_one_transaction', ['route' => route('tags.show', [$importJob->tag_id]), 'tag' => $importJob->tag->tag]);
} }
if ($count > 1 && null !== $importJob->tag_id) { if ($count > 1 && null !== $importJob->tag_id) {
$json['report_txt'] = trans('import.result_many_transactions', ['count' => $count,'route' => route('tags.show', [$importJob->tag_id]), 'tag' => $importJob->tag->tag ]); $json['report_txt'] = trans(
'import.result_many_transactions', ['count' => $count, 'route' => route('tags.show', [$importJob->tag_id]), 'tag' => $importJob->tag->tag]
);
} }
return response()->json($json); return response()->json($json);
} }
/** /**
* @param ImportJob $job * @param ImportJob $importJob
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function start(ImportJob $importJob): JsonResponse public function start(ImportJob $importJob): JsonResponse
{ {
// catch impossible status: // catch impossible status:
$allowed = ['ready_to_run', 'need_job_config']; $allowed = ['ready_to_run', 'need_job_config'];
if (null !== $importJob && !in_array($importJob->status, $allowed)) { if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error('Job is not ready.'); Log::error('Job is not ready.');
return response()->json(['status' => 'NOK', 'message' => 'JobStatusController::start expects state "ready_to_run".']); return response()->json(['status' => 'NOK', 'message' => 'JobStatusController::start expects state "ready_to_run".']);
@ -139,29 +127,13 @@ class JobStatusController extends Controller
$key = sprintf('import.routine.%s', $importProvider); $key = sprintf('import.routine.%s', $importProvider);
$className = config($key); $className = config($key);
if (null === $className || !class_exists($className)) { if (null === $className || !class_exists($className)) {
return response()->json(['status' => 'NOK', 'message' => sprintf('Cannot find import routine class for job of type "%s".', $importProvider)]); // @codeCoverageIgnoreStart
return response()->json(
['status' => 'NOK', 'message' => sprintf('Cannot find import routine class for job of type "%s".', $importProvider)]
);
// @codeCoverageIgnoreEnd
} }
// if the job is set to "provider_finished", we should be able to store transactions
// generated by the provider.
// otherwise, just continue.
if ($importJob->status === 'provider_finished') {
try {
$this->importFromJob($importJob);
} catch (FireflyException $e) {
$message = 'The import storage routine crashed: ' . $e->getMessage();
Log::error($message);
Log::error($e->getTraceAsString());
// set job errored out:
$this->repository->setStatus($importJob, 'error');
return response()->json(['status' => 'NOK', 'message' => $message]);
}
}
// set job to be running: // set job to be running:
$this->repository->setStatus($importJob, 'running'); $this->repository->setStatus($importJob, 'running');
@ -170,7 +142,7 @@ class JobStatusController extends Controller
$routine->setJob($importJob); $routine->setJob($importJob);
try { try {
$routine->run(); $routine->run();
} catch (FireflyException $e) { } catch (FireflyException|Exception $e) {
$message = 'The import routine crashed: ' . $e->getMessage(); $message = 'The import routine crashed: ' . $e->getMessage();
Log::error($message); Log::error($message);
Log::error($e->getTraceAsString()); Log::error($e->getTraceAsString());
@ -186,16 +158,20 @@ class JobStatusController extends Controller
} }
/** /**
* @param ImportJob $job * Store does three things:
*
* - Store the transactions.
* - Add them to a tag.
*
* @param ImportJob $importJob
* *
* @return JsonResponse * @return JsonResponse
* @throws FireflyException
*/ */
public function store(ImportJob $importJob): JsonResponse public function store(ImportJob $importJob): JsonResponse
{ {
// catch impossible status: // catch impossible status:
$allowed = ['provider_finished', 'storing_data']; // todo remove storing data. $allowed = ['provider_finished', 'storing_data'];
if (null !== $importJob && !in_array($importJob->status, $allowed)) { if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) {
Log::error('Job is not ready.'); Log::error('Job is not ready.');
return response()->json(['status' => 'NOK', 'message' => 'JobStatusController::start expects state "provider_finished".']); return response()->json(['status' => 'NOK', 'message' => 'JobStatusController::start expects state "provider_finished".']);
@ -205,7 +181,7 @@ class JobStatusController extends Controller
$this->repository->setStatus($importJob, 'storing_data'); $this->repository->setStatus($importJob, 'storing_data');
try { try {
$this->importFromJob($importJob); $this->storeTransactions($importJob);
} catch (FireflyException $e) { } catch (FireflyException $e) {
$message = 'The import storage routine crashed: ' . $e->getMessage(); $message = 'The import storage routine crashed: ' . $e->getMessage();
Log::error($message); Log::error($message);
@ -216,9 +192,9 @@ class JobStatusController extends Controller
return response()->json(['status' => 'NOK', 'message' => $message]); return response()->json(['status' => 'NOK', 'message' => $message]);
} }
// set storage to be finished:
$this->repository->setStatus($importJob, 'storage_finished');
// set job to be finished.
$this->repository->setStatus($importJob, 'finished');
// expect nothing from routine, just return OK to user. // expect nothing from routine, just return OK to user.
return response()->json(['status' => 'OK', 'message' => 'storage_finished']); return response()->json(['status' => 'OK', 'message' => 'storage_finished']);
@ -229,90 +205,15 @@ class JobStatusController extends Controller
* *
* @throws FireflyException * @throws FireflyException
*/ */
private function importFromJob(ImportJob $importJob): void private function storeTransactions(ImportJob $importJob): void
{ {
/** @var ImportArrayStorage $storage */
$storage = app(ImportArrayStorage::class);
$storage->setJob($importJob);
try { try {
$storage = new ImportArrayStorage($importJob); $storage->store();
$journals = $storage->store(); } catch (FireflyException|Exception $e) {
$extendedStatus = $importJob->extended_status;
$extendedStatus['count'] = $journals->count();
$this->repository->setExtendedStatus($importJob, $extendedStatus);
} catch (FireflyException|Exception|FatalThrowableError $e) {
throw new FireflyException($e->getMessage()); throw new FireflyException($e->getMessage());
} }
} }
// /**
// * @param ImportJob $job
// *
// * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
// */
// public function index(ImportJob $job)
// {
// $statuses = ['configured', 'running', 'finished', 'error'];
// if (!\in_array($job->status, $statuses)) {
// return redirect(route('import.configure', [$job->key]));
// }
// $subTitle = trans('import.status_sub_title');
// $subTitleIcon = 'fa-star';
//
// return view('import.status', compact('job', 'subTitle', 'subTitleIcon'));
// }
//
// /**
// * Show status of import job in JSON.
// *
// * @param ImportJob $job
// *
// * @return \Illuminate\Http\JsonResponse
// */
// public function json(ImportJob $job)
// {
// $result = [
// 'started' => false,
// 'finished' => false,
// 'running' => false,
// 'errors' => array_values($job->extended_status['errors']),
// 'percentage' => 0,
// 'show_percentage' => false,
// 'steps' => $job->extended_status['steps'],
// 'done' => $job->extended_status['done'],
// 'statusText' => trans('import.status_job_' . $job->status),
// 'status' => $job->status,
// 'finishedText' => '',
// ];
//
// if (0 !== $job->extended_status['steps']) {
// $result['percentage'] = round(($job->extended_status['done'] / $job->extended_status['steps']) * 100, 0);
// $result['show_percentage'] = true;
// }
// if ('finished' === $job->status) {
// $result['finished'] = true;
// $tagId = (int)$job->extended_status['tag'];
// if ($tagId !== 0) {
// /** @var TagRepositoryInterface $repository */
// $repository = app(TagRepositoryInterface::class);
// $tag = $repository->find($tagId);
// $count = $tag->transactionJournals()->count();
// $result['finishedText'] = trans(
// 'import.status_finished_job', ['count' => $count, 'link' => route('tags.show', [$tag->id, 'all']), 'tag' => $tag->tag]
// );
// }
//
// if ($tagId === 0) {
// $result['finishedText'] = trans('import.status_finished_no_tag'); // @codeCoverageIgnore
// }
// }
//
// if ('running' === $job->status) {
// $result['started'] = true;
// $result['running'] = true;
// }
// $result['percentage'] = $result['percentage'] > 100 ? 100 : $result['percentage'];
// Log::debug(sprintf('JOB STATUS: %d/%d', $result['done'], $result['steps']));
//
// return response()->json($result);
// }
} }

View File

@ -51,7 +51,6 @@ class PrerequisitesController extends Controller
function ($request, $next) { function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-archive'); app('view')->share('mainTitleIcon', 'fa-archive');
app('view')->share('title', trans('firefly.import_index_title')); app('view')->share('title', trans('firefly.import_index_title'));
app('view')->share('subTitleIcon', 'fa-check'); app('view')->share('subTitleIcon', 'fa-check');
$this->repository = app(ImportJobRepositoryInterface::class); $this->repository = app(ImportJobRepositoryInterface::class);
@ -59,7 +58,6 @@ class PrerequisitesController extends Controller
return $next($request); return $next($request);
} }
); );
$this->middleware(IsDemoUser::class);
} }
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,7 @@ namespace FireflyIII\Import\Routine;
use Carbon\Carbon; use Carbon\Carbon;
use DB; use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\FileProcessor\FileProcessorInterface; use FireflyIII\Import\FileProcessor\FileProcessorInterface;
use FireflyIII\Import\Storage\ImportStorage; use FireflyIII\Import\Storage\ImportStorage;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
@ -38,271 +39,295 @@ use Log;
*/ */
class FileRoutine implements RoutineInterface class FileRoutine implements RoutineInterface
{ {
/** @var Collection */ // /** @var Collection */
public $errors; // public $errors;
/** @var Collection */ // /** @var Collection */
public $journals; // public $journals;
/** @var int */ // /** @var int */
public $lines = 0; // public $lines = 0;
/** @var ImportJob */ // /** @var ImportJob */
private $job; // private $job;
//
/** @var ImportJobRepositoryInterface */ // /** @var ImportJobRepositoryInterface */
private $repository; // private $repository;
//
/** // /**
* ImportRoutine constructor. // * ImportRoutine constructor.
*/ // */
public function __construct() // public function __construct()
{ // {
$this->journals = new Collection; // $this->journals = new Collection;
$this->errors = new Collection; // $this->errors = new Collection;
} // }
//
/** // /**
* @return Collection // * @return Collection
*/ // */
public function getErrors(): Collection // public function getErrors(): Collection
{ // {
return $this->errors; // return $this->errors;
} // }
//
/** // /**
* @return Collection // * @return Collection
*/ // */
public function getJournals(): Collection // public function getJournals(): Collection
{ // {
return $this->journals; // return $this->journals;
} // }
//
/** // /**
* @return int // * @return int
*/ // */
public function getLines(): int // public function getLines(): int
{ // {
return $this->lines; // return $this->lines;
} // }
//
// /**
// *
// */
// public function run(): bool
// {
// if ('configured' !== $this->getStatus()) {
// Log::error(sprintf('Job %s is in state "%s" so it cannot be started.', $this->job->key, $this->getStatus()));
//
// return false;
// }
// set_time_limit(0);
// Log::info(sprintf('Start with import job %s', $this->job->key));
//
// // total steps: 6
// $this->setTotalSteps(6);
//
// $importObjects = $this->getImportObjects();
// $this->lines = $importObjects->count();
// $this->addStep();
//
// // total steps can now be extended. File has been scanned. 7 steps per line:
// $this->addTotalSteps(7 * $this->lines);
//
// // once done, use storage thing to actually store them:
// Log::info(sprintf('Returned %d valid objects from file processor', $this->lines));
//
// $storage = $this->storeObjects($importObjects);
// $this->addStep();
// Log::debug('Back in run()');
//
// Log::debug('Updated job...');
// Log::debug(sprintf('%d journals in $storage->journals', $storage->journals->count()));
// $this->journals = $storage->journals;
// $this->errors = $storage->errors;
//
// Log::debug('Going to call createImportTag()');
//
// // create tag, link tag to all journals:
// $this->createImportTag();
// $this->addStep();
//
// // update job:
// $this->setStatus('finished');
//
// Log::info(sprintf('Done with import job %s', $this->job->key));
//
// return true;
// }
//
// /**
// * @param ImportJob $job
// */
// public function setJob(ImportJob $job)
// {
// $this->job = $job;
// $this->repository = app(ImportJobRepositoryInterface::class);
// $this->repository->setUser($job->user);
// }
//
// /**
// * @return Collection
// */
// protected function getImportObjects(): Collection
// {
// $objects = new Collection;
// $fileType = $this->getConfig()['file-type'] ?? 'csv';
// // will only respond to "file"
// $class = config(sprintf('import.options.file.processors.%s', $fileType));
// /** @var FileProcessorInterface $processor */
// $processor = app($class);
// $processor->setJob($this->job);
//
// if ('configured' === $this->getStatus()) {
// // set job as "running"...
// $this->setStatus('running');
//
// Log::debug('Job is configured, start with run()');
// $processor->run();
// $objects = $processor->getObjects();
// }
//
// return $objects;
// }
//
// /**
// * Shorthand method.
// */
// private function addStep()
// {
// $this->repository->addStepsDone($this->job, 1);
// }
//
// /**
// * Shorthand
// *
// * @param int $steps
// */
// private function addTotalSteps(int $steps)
// {
// $this->repository->addTotalSteps($this->job, $steps);
// }
//
// /**
// *
// */
// private function createImportTag(): Tag
// {
// Log::debug('Now in createImportTag()');
//
// if ($this->journals->count() < 1) {
// Log::info(sprintf('Will not create tag, %d journals imported.', $this->journals->count()));
//
// return new Tag;
// }
// $this->addTotalSteps($this->journals->count() + 2);
//
// /** @var TagRepositoryInterface $repository */
// $repository = app(TagRepositoryInterface::class);
// $repository->setUser($this->job->user);
// $data = [
// 'tag' => trans('import.import_with_key', ['key' => $this->job->key]),
// 'date' => new Carbon,
// 'description' => null,
// 'latitude' => null,
// 'longitude' => null,
// 'zoomLevel' => null,
// 'tagMode' => 'nothing',
// ];
// $tag = $repository->store($data);
// $this->addStep();
// $extended = $this->getExtendedStatus();
// $extended['tag'] = $tag->id;
// $this->setExtendedStatus($extended);
//
// Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
// Log::debug('Looping journals...');
// $journalIds = $this->journals->pluck('id')->toArray();
// $tagId = $tag->id;
// foreach ($journalIds as $journalId) {
// Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
// DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
// $this->addStep();
// }
// Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $this->journals->count(), $tag->id, $tag->tag));
// $this->addStep();
//
// return $tag;
// }
//
// /**
// * Shorthand method
// *
// * @return array
// */
// private function getConfig(): array
// {
// return $this->repository->getConfiguration($this->job);
// }
//
// /**
// * @return array
// */
// private function getExtendedStatus(): array
// {
// return $this->repository->getExtendedStatus($this->job);
// }
//
// /**
// * Shorthand method.
// *
// * @return string
// */
// private function getStatus(): string
// {
// return $this->repository->getStatus($this->job);
// }
//
// /**
// * @param array $extended
// */
// private function setExtendedStatus(array $extended): void
// {
// $this->repository->setExtendedStatus($this->job, $extended);
// }
//
// /**
// * Shorthand
// *
// * @param string $status
// */
// private function setStatus(string $status): void
// {
// $this->repository->setStatus($this->job, $status);
// }
//
// /**
// * Shorthand
// *
// * @param int $steps
// */
// private function setTotalSteps(int $steps)
// {
// $this->repository->setTotalSteps($this->job, $steps);
// }
//
// /**
// * @param Collection $objects
// *
// * @return ImportStorage
// */
// private function storeObjects(Collection $objects): ImportStorage
// {
// $config = $this->getConfig();
// $storage = new ImportStorage;
// $storage->setJob($this->job);
// $storage->setDateFormat($config['date-format']);
// $storage->setObjects($objects);
// $storage->store();
// Log::info('Back in storeObjects()');
//
// return $storage;
// }
/** /**
* At the end of each run(), the import routine must set the job to the expected status.
* *
* The final status of the routine must be "provider_finished".
*
* @return bool
* @throws FireflyException
*/ */
public function run(): bool public function run(): void
{ {
if ('configured' !== $this->getStatus()) { // TODO: Implement run() method.
Log::error(sprintf('Job %s is in state "%s" so it cannot be started.', $this->job->key, $this->getStatus())); throw new NotImplementedException;
return false;
}
set_time_limit(0);
Log::info(sprintf('Start with import job %s', $this->job->key));
// total steps: 6
$this->setTotalSteps(6);
$importObjects = $this->getImportObjects();
$this->lines = $importObjects->count();
$this->addStep();
// total steps can now be extended. File has been scanned. 7 steps per line:
$this->addTotalSteps(7 * $this->lines);
// once done, use storage thing to actually store them:
Log::info(sprintf('Returned %d valid objects from file processor', $this->lines));
$storage = $this->storeObjects($importObjects);
$this->addStep();
Log::debug('Back in run()');
Log::debug('Updated job...');
Log::debug(sprintf('%d journals in $storage->journals', $storage->journals->count()));
$this->journals = $storage->journals;
$this->errors = $storage->errors;
Log::debug('Going to call createImportTag()');
// create tag, link tag to all journals:
$this->createImportTag();
$this->addStep();
// update job:
$this->setStatus('finished');
Log::info(sprintf('Done with import job %s', $this->job->key));
return true;
} }
/** /**
* @param ImportJob $job * @param ImportJob $job
*
* @return mixed
*/ */
public function setJob(ImportJob $job) public function setJob(ImportJob $job)
{ {
$this->job = $job; // TODO: Implement setJob() method.
$this->repository = app(ImportJobRepositoryInterface::class); throw new NotImplementedException;
$this->repository->setUser($job->user);
}
/**
* @return Collection
*/
protected function getImportObjects(): Collection
{
$objects = new Collection;
$fileType = $this->getConfig()['file-type'] ?? 'csv';
// will only respond to "file"
$class = config(sprintf('import.options.file.processors.%s', $fileType));
/** @var FileProcessorInterface $processor */
$processor = app($class);
$processor->setJob($this->job);
if ('configured' === $this->getStatus()) {
// set job as "running"...
$this->setStatus('running');
Log::debug('Job is configured, start with run()');
$processor->run();
$objects = $processor->getObjects();
}
return $objects;
}
/**
* Shorthand method.
*/
private function addStep()
{
$this->repository->addStepsDone($this->job, 1);
}
/**
* Shorthand
*
* @param int $steps
*/
private function addTotalSteps(int $steps)
{
$this->repository->addTotalSteps($this->job, $steps);
}
/**
*
*/
private function createImportTag(): Tag
{
Log::debug('Now in createImportTag()');
if ($this->journals->count() < 1) {
Log::info(sprintf('Will not create tag, %d journals imported.', $this->journals->count()));
return new Tag;
}
$this->addTotalSteps($this->journals->count() + 2);
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($this->job->user);
$data = [
'tag' => trans('import.import_with_key', ['key' => $this->job->key]),
'date' => new Carbon,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
$tag = $repository->store($data);
$this->addStep();
$extended = $this->getExtendedStatus();
$extended['tag'] = $tag->id;
$this->setExtendedStatus($extended);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$journalIds = $this->journals->pluck('id')->toArray();
$tagId = $tag->id;
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
$this->addStep();
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $this->journals->count(), $tag->id, $tag->tag));
$this->addStep();
return $tag;
}
/**
* Shorthand method
*
* @return array
*/
private function getConfig(): array
{
return $this->repository->getConfiguration($this->job);
}
/**
* @return array
*/
private function getExtendedStatus(): array
{
return $this->repository->getExtendedStatus($this->job);
}
/**
* Shorthand method.
*
* @return string
*/
private function getStatus(): string
{
return $this->repository->getStatus($this->job);
}
/**
* @param array $extended
*/
private function setExtendedStatus(array $extended): void
{
$this->repository->setExtendedStatus($this->job, $extended);
}
/**
* Shorthand
*
* @param string $status
*/
private function setStatus(string $status): void
{
$this->repository->setStatus($this->job, $status);
}
/**
* Shorthand
*
* @param int $steps
*/
private function setTotalSteps(int $steps)
{
$this->repository->setTotalSteps($this->job, $steps);
}
/**
* @param Collection $objects
*
* @return ImportStorage
*/
private function storeObjects(Collection $objects): ImportStorage
{
$config = $this->getConfig();
$storage = new ImportStorage;
$storage->setJob($this->job);
$storage->setDateFormat($config['date-format']);
$storage->setObjects($objects);
$storage->store();
Log::info('Back in storeObjects()');
return $storage;
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -3,21 +3,21 @@
namespace FireflyIII\Import\Storage; namespace FireflyIII\Import\Storage;
use Carbon\Carbon; use Carbon\Carbon;
use DB;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\TransactionJournalFactory; use FireflyIII\Factory\TransactionJournalFactory;
use FireflyIII\Helpers\Collector\JournalCollectorInterface; use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Helpers\Filter\InternalTransferFilter; use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\ImportJob; use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Tag; use FireflyIII\Models\Rule;
use FireflyIII\Models\Transaction; use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournalMeta; use FireflyIII\Models\TransactionJournalMeta;
use FireflyIII\Models\TransactionType; use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\TransactionRules\Processor;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Log; use Log;
use DB;
/** /**
* Creates new transactions based upon arrays. Will first check the array for duplicates. * Creates new transactions based upon arrays. Will first check the array for duplicates.
@ -40,11 +40,9 @@ class ImportArrayStorage
private $transfers; private $transfers;
/** /**
* ImportArrayStorage constructor.
*
* @param ImportJob $importJob * @param ImportJob $importJob
*/ */
public function __construct(ImportJob $importJob) public function setJob(ImportJob $importJob): void
{ {
$this->importJob = $importJob; $this->importJob = $importJob;
$this->countTransfers(); $this->countTransfers();
@ -56,104 +54,61 @@ class ImportArrayStorage
} }
/** /**
* Actually does the storing. * Actually does the storing. Does three things.
* - Store journals
* - Link to tag
* - Run rules (if set to)
* *
* @return Collection * @return Collection
* @throws FireflyException * @throws FireflyException
*/ */
public function store(): Collection public function store(): Collection
{ {
$count = count($this->importJob->transactions); // store transactions
Log::debug(sprintf('Now in store(). Count of items is %d', $count)); $this->setStatus('storing_data');
$toStore = []; $collection = $this->storeArray();
foreach ($this->importJob->transactions as $index => $transaction) { $this->setStatus('stored_data');
Log::debug(sprintf('Now at item %d out of %d', ($index + 1), $count));
$existingId = $this->hashExists($transaction); // link tag:
if (null !== $existingId) { $this->setStatus('linking_to_tag');
$this->logDuplicateObject($transaction, $existingId); $this->linkToTag($collection);
$this->repository->addErrorMessage( $this->setStatus('linked_to_tag');
$this->importJob, sprintf(
'Entry #%d ("%s") could not be imported. It already exists.', // run rules, if configured to.
$index, $transaction['description'] $config = $this->importJob->configuration;
) if (isset($config['apply-rules']) && $config['apply-rules'] === true) {
); $this->setStatus('applying_rules');
continue; $this->applyRules($collection);
} $this->setStatus('rules_applied');
if ($this->checkForTransfers) {
if ($this->transferExists($transaction)) {
$this->logDuplicateTransfer($transaction);
$this->repository->addErrorMessage(
$this->importJob, sprintf(
'Entry #%d ("%s") could not be imported. Such a transfer already exists.',
$index,
$transaction['description']
)
);
continue;
}
}
$toStore[] = $transaction;
} }
if (count($toStore) === 0) {
Log::info('No transactions to store left!');
return new Collection;
}
Log::debug('Going to store...');
// now actually store them:
$collection = new Collection;
/** @var TransactionJournalFactory $factory */
$factory = app(TransactionJournalFactory::class);
$factory->setUser($this->importJob->user);
foreach ($toStore as $store) {
// convert the date to an object:
$store['date'] = Carbon::createFromFormat('Y-m-d', $store['date']);
// store the journal.
$collection->push($factory->create($store));
}
Log::debug('DONE storing!');
// create tag and append journals:
$this->createTag($collection);
return $collection; return $collection;
} }
/** /**
* @param Collection $collection * @param Collection $collection
*
* @throws FireflyException
*/ */
private function createTag(Collection $collection): void private function applyRules(Collection $collection): void
{ {
$rules = $this->getRules();
if ($rules->count() > 0) {
foreach ($collection as $journal) {
$rules->each(
function (Rule $rule) use ($journal) {
Log::debug(sprintf('Going to apply rule #%d to journal %d.', $rule->id, $journal->id));
$processor = Processor::make($rule);
$processor->handleTransactionJournal($journal);
if ($rule->stop_processing) {
return false;
}
/** @var TagRepositoryInterface $repository */ return true;
$repository = app(TagRepositoryInterface::class); }
$repository->setUser($this->importJob->user); );
$data = [ }
'tag' => trans('import.import_with_key', ['key' => $this->importJob->key]),
'date' => new Carbon,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
$tag = $repository->store($data);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$journalIds = $collection->pluck('id')->toArray();
$tagId = $tag->id;
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
} }
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $collection->count(), $tag->id, $tag->tag));
$this->repository->setTag($this->importJob, $tag);
} }
/** /**
@ -161,8 +116,10 @@ class ImportArrayStorage
*/ */
private function countTransfers(): void private function countTransfers(): void
{ {
/** @var array $array */
$array = $this->importJob->transactions;
$count = 0; $count = 0;
foreach ($this->importJob->transactions as $transaction) { foreach ($array as $transaction) {
if (strtolower(TransactionType::TRANSFER) === $transaction['type']) { if (strtolower(TransactionType::TRANSFER) === $transaction['type']) {
$count++; $count++;
} }
@ -177,6 +134,29 @@ class ImportArrayStorage
} }
/**
* @return Collection
*/
private function getRules(): Collection
{
/** @var Collection $set */
$set = Rule::distinct()
->where('rules.user_id', $this->importJob->user_id)
->leftJoin('rule_groups', 'rule_groups.id', '=', 'rules.rule_group_id')
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_groups.active', 1)
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->orderBy('rule_groups.order', 'ASC')
->orderBy('rules.order', 'ASC')
->get(['rules.*', 'rule_groups.order']);
Log::debug(sprintf('Found %d user rules.', $set->count()));
return $set;
}
/** /**
* Get the users transfers, so they can be compared to whatever the user is trying to import. * Get the users transfers, so they can be compared to whatever the user is trying to import.
*/ */
@ -192,7 +172,6 @@ class ImportArrayStorage
} }
/** /**
* @param array $transaction * @param array $transaction
* *
@ -222,6 +201,39 @@ class ImportArrayStorage
return (int)$entry->transaction_journal_id; return (int)$entry->transaction_journal_id;
} }
/**
* @param Collection $collection
*/
private function linkToTag(Collection $collection): void
{
/** @var TagRepositoryInterface $repository */
$repository = app(TagRepositoryInterface::class);
$repository->setUser($this->importJob->user);
$data = [
'tag' => trans('import.import_with_key', ['key' => $this->importJob->key]),
'date' => new Carbon,
'description' => null,
'latitude' => null,
'longitude' => null,
'zoomLevel' => null,
'tagMode' => 'nothing',
];
$tag = $repository->store($data);
Log::debug(sprintf('Created tag #%d ("%s")', $tag->id, $tag->tag));
Log::debug('Looping journals...');
$journalIds = $collection->pluck('id')->toArray();
$tagId = $tag->id;
foreach ($journalIds as $journalId) {
Log::debug(sprintf('Linking journal #%d to tag #%d...', $journalId, $tagId));
DB::table('tag_transaction_journal')->insert(['transaction_journal_id' => $journalId, 'tag_id' => $tagId]);
}
Log::info(sprintf('Linked %d journals to tag #%d ("%s")', $collection->count(), $tag->id, $tag->tag));
$this->repository->setTag($this->importJob, $tag);
}
/** /**
* @param array $transaction * @param array $transaction
* @param int $existingId * @param int $existingId
@ -255,6 +267,84 @@ class ImportArrayStorage
); );
} }
/**
* Shorthand method to quickly set job status
*
* @param string $status
*/
private function setStatus(string $status): void
{
$this->repository->setStatus($this->importJob, $status);
}
/**
* Store array as journals.
*
* @return Collection
* @throws FireflyException
*/
private function storeArray(): Collection
{
/** @var array $array */
$array = $this->importJob->transactions;
$count = \count($array);
$toStore = [];
Log::debug(sprintf('Now in store(). Count of items is %d', $count));
foreach ($array as $index => $transaction) {
Log::debug(sprintf('Now at item %d out of %d', $index + 1, $count));
$existingId = $this->hashExists($transaction);
if (null !== $existingId) {
$this->logDuplicateObject($transaction, $existingId);
$this->repository->addErrorMessage(
$this->importJob, sprintf(
'Entry #%d ("%s") could not be imported. It already exists.',
$index, $transaction['description']
)
);
continue;
}
if ($this->checkForTransfers) {
if ($this->transferExists($transaction)) {
$this->logDuplicateTransfer($transaction);
$this->repository->addErrorMessage(
$this->importJob, sprintf(
'Entry #%d ("%s") could not be imported. Such a transfer already exists.',
$index,
$transaction['description']
)
);
continue;
}
}
$toStore[] = $transaction;
}
if (\count($toStore) === 0) {
Log::info('No transactions to store left!');
return new Collection;
}
Log::debug('Going to store...');
// now actually store them:
$collection = new Collection;
/** @var TransactionJournalFactory $factory */
$factory = app(TransactionJournalFactory::class);
$factory->setUser($this->importJob->user);
foreach ($toStore as $store) {
// convert the date to an object:
$store['date'] = Carbon::createFromFormat('Y-m-d', $store['date']);
// store the journal.
$collection->push($factory->create($store));
}
Log::debug('DONE storing!');
return $collection;
}
/** /**
* Check if a transfer exists. * Check if a transfer exists.
* *

View File

@ -36,7 +36,7 @@ class StageAhoyHandler
*/ */
public function run(): void public function run(): void
{ {
for ($i = 0; $i < 15; $i++) { for ($i = 0; $i < 5; $i++) {
Log::debug(sprintf('Am now in stage AHOY hander, sleeping... (%d)', $i)); Log::debug(sprintf('Am now in stage AHOY hander, sleeping... (%d)', $i));
sleep(1); sleep(1);
} }

View File

@ -36,7 +36,7 @@ class StageNewHandler
*/ */
public function run(): void public function run(): void
{ {
for ($i = 0; $i < 15; $i++) { for ($i = 0; $i < 5; $i++) {
Log::debug(sprintf('Am now in stage new hander, sleeping... (%d)', $i)); Log::debug(sprintf('Am now in stage new hander, sleeping... (%d)', $i));
sleep(1); sleep(1);
} }

View File

@ -39,7 +39,7 @@ use FireflyIII\Import\Routine\SpectreRoutine;
return [ return [
'enabled' => [ 'enabled' => [
'fake' => true, 'fake' => false,
'file' => true, 'file' => true,
'bunq' => true, 'bunq' => true,
'spectre' => true, 'spectre' => true,

View File

@ -30,8 +30,8 @@ class ChangesForV474 extends Migration
function (Blueprint $table) { function (Blueprint $table) {
$table->string('provider', 50)->after('file_type')->default(''); $table->string('provider', 50)->after('file_type')->default('');
$table->string('stage', 50)->after('status')->default(''); $table->string('stage', 50)->after('status')->default('');
$table->longText('transactions')->after('extended_status'); $table->longText('transactions')->after('extended_status')->nullable();
$table->longText('errors')->after('transactions'); $table->longText('errors')->after('transactions')->nullable();
$table->integer('tag_id', false, true)->nullable()->after('user_id'); $table->integer('tag_id', false, true)->nullable()->after('user_id');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('set null'); $table->foreign('tag_id')->references('id')->on('tags')->onDelete('set null');

View File

@ -21,8 +21,8 @@
/** global: jobStatusUri */ /** global: jobStatusUri */
var timeOutId; var timeOutId;
var hasStartedJob = false; var jobRunRoutineStarted = false;
var jobStorageStarted = false; var jobStorageRoutineStarted = false;
var checkInitialInterval = 1000; var checkInitialInterval = 1000;
var checkNextInterval = 500; var checkNextInterval = 500;
var maxLoops = 60; var maxLoops = 60;
@ -53,18 +53,12 @@ function reportJobJSONDone(data) {
switch (data.status) { switch (data.status) {
case "ready_to_run": case "ready_to_run":
if (startCount > 0) { if (startCount > 0) {
hasStartedJob = false; jobRunRoutineStarted = false;
} }
startCount++; startCount++;
sendJobPOSTStart(); sendJobPOSTStart();
recheckJobJSONStatus(); recheckJobJSONStatus();
break; break;
case "running":
case "storing_data":
showProgressBox(data.status);
recheckJobJSONStatus();
break;
case "need_job_config": case "need_job_config":
// redirect user to configuration for this job. // redirect user to configuration for this job.
window.location.replace(jobConfigurationUri); window.location.replace(jobConfigurationUri);
@ -74,11 +68,14 @@ function reportJobJSONDone(data) {
sendJobPOSTStore(); sendJobPOSTStore();
recheckJobJSONStatus(); recheckJobJSONStatus();
break; break;
case "storage_finished":
case "finished": case "finished":
showJobResults(data); showJobResults(data);
break; break;
default: default:
console.error('Cannot handle status ' + data.status); console.warn('No specific action for status ' + data.status);
showProgressBox(data.status);
recheckJobJSONStatus();
} }
} }
@ -129,12 +126,12 @@ function recheckJobJSONStatus() {
*/ */
function sendJobPOSTStart() { function sendJobPOSTStart() {
console.log('In sendJobPOSTStart()'); console.log('In sendJobPOSTStart()');
if (hasStartedJob) { if (jobRunRoutineStarted) {
console.log('Import job already started!'); console.log('Import job already started!');
return; return;
} }
console.log('Job was started'); console.log('Job was started');
hasStartedJob = true; jobRunRoutineStarted = true;
$.post(jobStartUri, {_token: token}).fail(reportJobPOSTFailure).done(reportJobPOSTDone) $.post(jobStartUri, {_token: token}).fail(reportJobPOSTFailure).done(reportJobPOSTDone)
} }
@ -143,12 +140,12 @@ function sendJobPOSTStart() {
*/ */
function sendJobPOSTStore() { function sendJobPOSTStore() {
console.log('In sendJobPOSTStore()'); console.log('In sendJobPOSTStore()');
if (jobStorageStarted) { if (jobStorageRoutineStarted) {
console.log('Store job already started!'); console.log('Store job already started!');
return; return;
} }
console.log('Storage job has started!'); console.log('Storage job has started!');
jobStorageStarted = true; jobStorageRoutineStarted = true;
$.post(jobStorageStartUri, {_token: token}).fail(reportJobPOSTFailure).done(reportJobPOSTDone) $.post(jobStorageStartUri, {_token: token}).fail(reportJobPOSTFailure).done(reportJobPOSTDone)
} }
@ -185,14 +182,26 @@ function showProgressBox(status) {
// hide initial status box: // hide initial status box:
$('.status_initial').hide(); $('.status_initial').hide();
if (status === 'running' || status === 'ready_to_run') {
$('#import-status-txt').text(langImportRunning);
} else {
$('#import-status-txt').text(langImportStoring);
}
// show running box: // show running box:
$('.status_running').show(); $('.status_running').show();
if (status === 'running' || status === 'ready_to_run') {
$('#import-status-txt').text(langImportRunning);
return;
}
if (status === 'storing_data' || status === 'storage_finished' || status === 'stored_data') {
$('#import-status-txt').text(langImportStoring);
return;
}
if (status === 'applying_rules' || status === 'linking_to_tag' || status === 'linked_to_tag' || status === 'rules_applied') {
$('#import-status-txt').text(langImportRules);
return;
}
$('#import-status-txt').text('Job status: ' + status);
} }
/** /**

View File

@ -79,7 +79,7 @@ return [
'job_config_fake_song_title' => 'Enter song name', 'job_config_fake_song_title' => 'Enter song name',
'job_config_fake_song_text' => 'Mention the song "Golden years" to continue with the fake import.', 'job_config_fake_song_text' => 'Mention the song "Golden years" to continue with the fake import.',
'job_config_fake_album_title' => 'Enter album name', 'job_config_fake_album_title' => 'Enter album name',
'job_config_fake_album_text' => 'Some import routines require extra data halfway through the import. In the case of the fake import provider, you must answer some weird questions. In this case, enter "station to station" to continue.', 'job_config_fake_album_text' => 'Some import routines require extra data halfway through the import. In the case of the fake import provider, you must answer some weird questions. Enter "Station to station" to continue.',
// import status page: // import status page:
'import_with_key' => 'Import with key \':key\'', 'import_with_key' => 'Import with key \':key\'',
@ -88,6 +88,7 @@ return [
'status_running_title' => 'The import is running', 'status_running_title' => 'The import is running',
'status_job_running' => 'Please wait, running the import...', 'status_job_running' => 'Please wait, running the import...',
'status_job_storing' => 'Please wait, storing data...', 'status_job_storing' => 'Please wait, storing data...',
'status_job_rules' => 'Please wait, running rules...',
'status_fatal_title' => 'Fatal error', 'status_fatal_title' => 'Fatal error',
'status_fatal_text' => 'The import has suffered from an error it could not recover from. Apologies!', 'status_fatal_text' => 'The import has suffered from an error it could not recover from. Apologies!',
'status_fatal_more' => 'This (possibly very cryptic) error message is complemented by log files, which you can find on your hard drive, or in the Docker container where you run Firefly III from.', 'status_fatal_more' => 'This (possibly very cryptic) error message is complemented by log files, which you can find on your hard drive, or in the Docker container where you run Firefly III from.',

View File

@ -178,6 +178,7 @@
// import is running: // import is running:
var langImportRunning = '{{ trans('import.status_job_running') }}'; var langImportRunning = '{{ trans('import.status_job_running') }}';
var langImportStoring = '{{ trans('import.status_job_storing') }}'; var langImportStoring = '{{ trans('import.status_job_storing') }}';
var langImportRules = '{{ trans('import.status_job_rules') }}';
// some useful translations. // some useful translations.
{#var langImportTimeOutError = '(time out thing)';#} {#var langImportTimeOutError = '(time out thing)';#}

View File

@ -116,7 +116,7 @@ class ExportControllerTest extends TestCase
$journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal); $journalRepos->shouldReceive('firstNull')->once()->andReturn(new TransactionJournal);
$repository->shouldReceive('create')->andReturn($job); $repository->shouldReceive('create')->andReturn($job);
$repository->shouldReceive('cleanup'); $repository->shouldReceive('cleanup');
$accountRepos->shouldReceive('getAccountsByType')->withArgs([[AccountType::DEFAULT, AccountType::ASSET]])->andReturn(new Collection); $accountRepos->shouldReceive('getAccountsByType')->withArgs([[AccountType::ASSET, AccountType::DEFAULT]])->andReturn(new Collection);
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('export.index')); $response = $this->get(route('export.index'));

View File

@ -1,131 +0,0 @@
<?php
/**
* ConfigurationControllerTest.php
* Copyright (c) 2017 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\Feature\Controllers\Import;
use FireflyIII\Import\Configuration\FileConfigurator;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Log;
use Tests\TestCase;
/**
* Class AccountControllerTest
*
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class ConfigurationControllerTest extends TestCase
{
/**
*
*/
public function setUp()
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::__construct
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::index
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::makeConfigurator
*/
public function testIndex()
{
/** @var ImportJob $job */
$job = $this->user()->importJobs()->where('key', 'configuring')->first();
$configurator = $this->mock(FileConfigurator::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('isJobConfigured')->once()->andReturn(false);
$configurator->shouldReceive('getNextView')->once()->andReturn('error'); // does not matter which view is returned.
$configurator->shouldReceive('getNextData')->once()->andReturn([]);
$repository->shouldReceive('updateStatus')->once();
$this->be($this->user());
$response = $this->get(route('import.configure', [$job->key]));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::__construct
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::index
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::makeConfigurator
*/
public function testIndexConfigured()
{
/** @var ImportJob $job */
$job = $this->user()->importJobs()->where('key', 'configured')->first();
$configurator = $this->mock(FileConfigurator::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('isJobConfigured')->once()->andReturn(true);
$repository->shouldReceive('updateStatus')->once();
$this->be($this->user());
$response = $this->get(route('import.configure', [$job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.status', [$job->key]));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::post
*/
public function testPost()
{
/** @var ImportJob $job */
$job = $this->user()->importJobs()->where('key', 'configuring')->first();
$data = ['some' => 'config'];
$configurator = $this->mock(FileConfigurator::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('isJobConfigured')->once()->andReturn(false);
$configurator->shouldReceive('configureJob')->once()->withArgs([$data]);
$configurator->shouldReceive('getWarningMessage')->once()->andReturn('Some warning');
$this->be($this->user());
$response = $this->post(route('import.configure.post', [$job->key]), $data);
$response->assertStatus(302);
$response->assertRedirect(route('import.configure', [$job->key]));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\ConfigurationController::post
*/
public function testPostConfigured()
{
/** @var ImportJob $job */
$job = $this->user()->importJobs()->where('key', 'configuring')->first();
$data = ['some' => 'config'];
$configurator = $this->mock(FileConfigurator::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('isJobConfigured')->once()->andReturn(true);
$this->be($this->user());
$response = $this->post(route('import.configure.post', [$job->key]), $data);
$response->assertStatus(302);
$response->assertRedirect(route('import.status', [$job->key]));
}
}

View File

@ -22,13 +22,18 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Import; namespace Tests\Feature\Controllers\Import;
use FireflyIII\Import\Routine\FileRoutine; use FireflyIII\Import\Prerequisites\BunqPrerequisites;
use FireflyIII\Import\Prerequisites\FakePrerequisites;
use FireflyIII\Import\Prerequisites\FilePrerequisites;
use FireflyIII\Import\Prerequisites\SpectrePrerequisites;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Log; use Log;
use Mockery;
use Tests\TestCase; use Tests\TestCase;
/** /**
* Class AccountControllerTest * Class IndexControllerTest
* *
* @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
@ -46,73 +51,85 @@ class IndexControllerTest extends TestCase
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Import\IndexController::create * @covers \FireflyIII\Http\Controllers\Import\IndexController
*/ */
public function testCreate() public function testCreateFake()
{ {
$job = $this->user()->importJobs()->where('key', 'new')->first(); // mock stuff:
$repository = $this->mock(ImportJobRepositoryInterface::class); $repository = $this->mock(ImportJobRepositoryInterface::class);
$repository->shouldReceive('create')->withArgs(['file'])->andReturn($job); $fakePrerequisites = $this->mock(FakePrerequisites::class);
// fake job:
$importJob = new ImportJob;
$importJob->provider = 'fake';
$importJob->key = 'fake_job_1';
// mock call:
$repository->shouldReceive('create')->withArgs(['fake'])->andReturn($importJob);
$fakePrerequisites->shouldReceive('isComplete')->once()->andReturn(false);
$fakePrerequisites->shouldReceive('setUser')->once();
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('import.create-job', ['file'])); $response = $this->get(route('import.create', ['fake']));
$response->assertStatus(302); $response->assertStatus(302);
$response->assertRedirect(route('import.configure', ['new'])); // expect a redirect to prerequisites
$response->assertRedirect(route('import.prerequisites.index', ['fake', 'fake_job_1']));
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Import\IndexController::download * @covers \FireflyIII\Http\Controllers\Import\IndexController
*/ */
public function testDownload() public function testCreateFakeNoPrereq()
{ {
$repository = $this->mock(ImportJobRepositoryInterface::class); // mock stuff:
//$job = $this->user()->importJobs()->where('key', 'testImport')->first(); $repository = $this->mock(ImportJobRepositoryInterface::class);
$fakePrerequisites = $this->mock(FakePrerequisites::class);
// fake job:
$importJob = new ImportJob;
$importJob->provider = 'fake';
$importJob->key = 'fake_job_2';
// mock call:
$repository->shouldReceive('create')->withArgs(['fake'])->andReturn($importJob);
$fakePrerequisites->shouldReceive('isComplete')->once()->andReturn(true);
$fakePrerequisites->shouldReceive('setUser')->once();
$repository->shouldReceive('setStatus')->withArgs([Mockery::any(), 'has_prereq'])->andReturn($importJob)->once();
$this->be($this->user()); $this->be($this->user());
$response = $this->get(route('import.download', ['testImport'])); $response = $this->get(route('import.create', ['fake']));
$response->assertStatus(200); $response->assertStatus(302);
// expect a redirect to prerequisites
$response->assertRedirect(route('import.job.configuration.index', ['fake_job_2']));
} }
/**
* @covers \FireflyIII\Http\Controllers\Import\IndexController::__construct
* @covers \FireflyIII\Http\Controllers\Import\IndexController::index
*/
public function testIndex() public function testIndex()
{ {
$repository = $this->mock(ImportJobRepositoryInterface::class);
$this->be($this->user()); $this->be($this->user());
// fake prerequisites providers:
$fake = $this->mock(FakePrerequisites::class);
$file = $this->mock(FilePrerequisites::class);
$bunq = $this->mock(BunqPrerequisites::class);
$spectre = $this->mock(SpectrePrerequisites::class);
// call methods:
$fake->shouldReceive('setUser')->once();
$file->shouldReceive('setUser')->once();
$bunq->shouldReceive('setUser')->once();
$spectre->shouldReceive('setUser')->once();
$fake->shouldReceive('isComplete')->once()->andReturn(true);
$file->shouldReceive('isComplete')->once()->andReturn(true);
$bunq->shouldReceive('isComplete')->once()->andReturn(true);
$spectre->shouldReceive('isComplete')->once()->andReturn(true);
$response = $this->get(route('import.index')); $response = $this->get(route('import.index'));
$response->assertStatus(200); $response->assertStatus(200);
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Import\IndexController::start
*/
public function testStart()
{
$repository = $this->mock(ImportJobRepositoryInterface::class);
$routine = $this->mock(FileRoutine::class);
$routine->shouldReceive('setJob')->once();
$routine->shouldReceive('run')->once()->andReturn(true);
$this->be($this->user());
$response = $this->post(route('import.start', ['configured']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\IndexController::start
* @expectedExceptionMessage Job did not complete successfully.
*/
public function testStartFailed()
{
$repository = $this->mock(ImportJobRepositoryInterface::class);
$routine = $this->mock(FileRoutine::class);
$routine->shouldReceive('setJob')->once();
$routine->shouldReceive('run')->once()->andReturn(false);
$this->be($this->user());
$response = $this->post(route('import.start', ['configured']));
$response->assertStatus(500);
} }
} }

View File

@ -0,0 +1,228 @@
<?php
/**
* JobConfigurationControllerTest.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\Feature\Controllers\Import;
use FireflyIII\Import\JobConfiguration\FakeJobConfiguration;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Support\MessageBag;
use Log;
use Mockery;
use Tests\TestCase;
/**
* Class JobConfigurationControllerTest
*/
class JobConfigurationControllerTest extends TestCase
{
/**
*
*/
public function setUp()
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobConfigurationController
*/
public function testIndex(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Afake_job_' . random_int(1, 1000);
$job->status = 'has_prereq';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
// mock repositories and configuration handling classes:
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator = $this->mock(FakeJobConfiguration::class);
// mock calls:
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('configurationComplete')->once()->andReturn(false);
$configurator->shouldReceive('getNextView')->once()->andReturn('import.fake.apply-rules');
$configurator->shouldReceive('getNextData')->once()
->andReturn(['rulesOptions' => [1 => 'Y', 0 => 'N',],]);
$this->be($this->user());
$response = $this->get(route('import.job.configuration.index', [$job->key]));
$response->assertStatus(200);
// expect a redirect to prerequisites
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobConfigurationController
*/
public function testIndexBadState(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Bfake_job_' . random_int(1, 1000);
$job->status = 'some_bad_state';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
// mock repositories and configuration handling classes:
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator = $this->mock(FakeJobConfiguration::class);
// mock calls:
$this->be($this->user());
$response = $this->get(route('import.job.configuration.index', [$job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.index'));
$response->assertSessionHas('error', 'To access this page, your import job cannot have status "some_bad_state".');
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobConfigurationController
*/
public function testIndexComplete(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Cfake_job_' . random_int(1, 1000);
$job->status = 'has_prereq';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
// mock repositories and configuration handling classes:
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator = $this->mock(FakeJobConfiguration::class);
// mock calls:
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('configurationComplete')->once()->andReturn(true);
$repository->shouldReceive('updateStatus')->withArgs([Mockery::any(), 'ready_to_run']);
$this->be($this->user());
$response = $this->get(route('import.job.configuration.index', [$job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.job.status.index', [$job->key]));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobConfigurationController
*/
public function testPost(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Dfake_job_' . random_int(1, 1000);
$job->status = 'has_prereq';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
$messages = new MessageBag;
$messages->add('some', 'srrange message');
// mock repositories and configuration handling classes:
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator = $this->mock(FakeJobConfiguration::class);
// mock calls:
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('configurationComplete')->once()->andReturn(false);
$configurator->shouldReceive('configureJob')->withArgs([[]])->once()->andReturn($messages);
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.configuration.post', [$job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.job.configuration.index', [$job->key]));
$response->assertSessionHas('warning', $messages->first());
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobConfigurationController
*/
public function testPostComplete(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Efake_job_' . random_int(1, 1000);
$job->status = 'has_prereq';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
// mock repositories and configuration handling classes:
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator = $this->mock(FakeJobConfiguration::class);
// mock calls:
$configurator->shouldReceive('setJob')->once();
$configurator->shouldReceive('configurationComplete')->once()->andReturn(true);
$repository->shouldReceive('updateStatus')->withArgs([Mockery::any(), 'ready_to_run']);
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.configuration.post', [$job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.job.status.index', [$job->key]));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobConfigurationController
*/
public function testPostBadState(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Ffake_job_' . random_int(1, 1000);
$job->status = 'some_bad_state';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
$messages = new MessageBag;
$messages->add('some', 'srrange message');
// mock repositories and configuration handling classes:
$repository = $this->mock(ImportJobRepositoryInterface::class);
$configurator = $this->mock(FakeJobConfiguration::class);
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.configuration.post', [$job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.index'));
$response->assertSessionHas('error', 'To access this page, your import job cannot have status "some_bad_state".');
}
}

View File

@ -0,0 +1,375 @@
<?php
/**
* JobStatusControllerTest.php
* Copyright (c) 2017 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\Feature\Controllers\Import;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Import\Routine\FakeRoutine;
use FireflyIII\Import\Storage\ImportArrayStorage;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\Tag;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Log;
use Mockery;
use Tests\TestCase;
/**
* Class JobStatusControllerTest
*
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class JobStatusControllerTest extends TestCase
{
/**
*
*/
public function setUp()
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testIndex(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Afake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->file_type = '';
$job->save();
// call thing.
$this->be($this->user());
$response = $this->get(route('import.job.status.index', [$job->key]));
$response->assertStatus(200);
$response->assertSee('<ol class="breadcrumb">');
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testJson(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Bfake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// call thing.
$this->be($this->user());
$response = $this->get(route('import.job.status.json', [$job->key]));
$response->assertStatus(200);
$response->assertSee(
'No transactions have been imported. Perhaps they were all duplicates is simply no transactions where present to be imported. Perhaps the error message below can tell you what happened.'
);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testJsonWithTag(): void
{
$tag = $this->user()->tags()->first();
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Cfake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->tag()->associate($tag);
$job->save();
// call thing.
$this->be($this->user());
$response = $this->get(route('import.job.status.json', [$job->key]));
$response->assertStatus(200);
$response->assertSee(
'No transactions have been imported. Perhaps they were all duplicates is simply no transactions where present to be imported. Perhaps the error message below can tell you what happened.'
);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testJsonWithTagManyJournals(): void
{
/** @var Tag $tag */
$tag = $this->user()->tags()->first();
$journal = $this->user()->transactionJournals()->first();
$second = $this->user()->transactionJournals()->where('id', '!=', $journal->id)->first();
$tag->transactionJournals()->sync([$journal->id, $second->id]);
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Dfake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->tag()->associate($tag);
$job->save();
// call thing.
$this->be($this->user());
$response = $this->get(route('import.job.status.json', [$job->key]));
$response->assertStatus(200);
$response->assertSee(
'Firefly III has imported 2 transactions. They are stored under tag <a href=\"http:\/\/localhost\/tags\/show\/' . $tag->id
);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testJsonWithTagOneJournal(): void
{
/** @var Tag $tag */
$tag = $this->user()->tags()->first();
$journal = $this->user()->transactionJournals()->first();
$tag->transactionJournals()->sync([$journal->id]);
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Efake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->tag()->associate($tag);
$job->save();
// call thing.
$this->be($this->user());
$response = $this->get(route('import.job.status.json', [$job->key]));
$response->assertStatus(200);
$response->assertSee(
'Exactly one transaction has been imported. It is stored under tag <a href=\"http:\/\/localhost\/tags\/show\/' . $tag->id
);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStart(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Ffake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$repository = $this->mock(ImportJobRepositoryInterface::class);
$routine = $this->mock(FakeRoutine::class);
// mock calls:
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'running']);
$routine->shouldReceive('setJob')->once();
$routine->shouldReceive('run')->once();
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.start', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'OK', 'message' => 'stage_finished']);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStartException(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Gfake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$repository = $this->mock(ImportJobRepositoryInterface::class);
$routine = $this->mock(FakeRoutine::class);
// mock calls:
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'running']);
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'error']);
$routine->shouldReceive('setJob')->once();
$routine->shouldReceive('run')->andThrow(new Exception('Unknown exception'));
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.start', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'NOK', 'message' => 'The import routine crashed: Unknown exception']);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStartFireflyException(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Hfake_job_' . random_int(1, 1000);
$job->status = 'ready_to_run';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$repository = $this->mock(ImportJobRepositoryInterface::class);
$routine = $this->mock(FakeRoutine::class);
// mock calls:
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'running']);
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'error']);
$routine->shouldReceive('setJob')->once();
$routine->shouldReceive('run')->andThrow(new FireflyException('Unknown exception'));
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.start', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'NOK', 'message' => 'The import routine crashed: Unknown exception']);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStartInvalidState(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Ifake_job_' . random_int(1, 1000);
$job->status = 'bad_state';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// call thing.
$this->be($this->user());
$response = $this->post(route('import.job.start', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'NOK', 'message' => 'JobStatusController::start expects state "ready_to_run".']);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStore(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Jfake_job_' . random_int(1, 1000);
$job->status = 'provider_finished';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$repository = $this->mock(ImportJobRepositoryInterface::class);
$storage = $this->mock(ImportArrayStorage::class);
// mock calls:
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'storing_data']);
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'storage_finished']);
$storage->shouldReceive('setJob')->once();
$storage->shouldReceive('store')->once();
$this->be($this->user());
$response = $this->post(route('import.job.store', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'OK', 'message' => 'storage_finished']);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStoreInvalidState(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Kfake_job_' . random_int(1, 1000);
$job->status = 'some_bad_state';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
$this->be($this->user());
$response = $this->post(route('import.job.store', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'NOK', 'message' => 'JobStatusController::start expects state "provider_finished".']);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\JobStatusController
*/
public function testStoreException(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'Lfake_job_' . random_int(1, 1000);
$job->status = 'provider_finished';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$repository = $this->mock(ImportJobRepositoryInterface::class);
$storage = $this->mock(ImportArrayStorage::class);
// mock calls:
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'storing_data']);
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'error']);
$storage->shouldReceive('setJob')->once();
$storage->shouldReceive('store')->once()->andThrow(new FireflyException('Some storage exception.'));
$this->be($this->user());
$response = $this->post(route('import.job.store', [$job->key]));
$response->assertStatus(200);
$response->assertExactJson(['status' => 'NOK', 'message' => 'The import storage routine crashed: Some storage exception.']);
}
}

View File

@ -22,9 +22,12 @@ declare(strict_types=1);
namespace Tests\Feature\Controllers\Import; namespace Tests\Feature\Controllers\Import;
use FireflyIII\Import\Prerequisites\FilePrerequisites; use FireflyIII\Import\Prerequisites\FakePrerequisites;
use FireflyIII\Models\ImportJob;
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
use Illuminate\Support\MessageBag; use Illuminate\Support\MessageBag;
use Log; use Log;
use Mockery;
use Tests\TestCase; use Tests\TestCase;
/** /**
@ -46,74 +49,191 @@ class PrerequisitesControllerTest extends TestCase
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController::__construct * @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController::index
*/ */
public function testIndex() public function testIndex(): void
{ {
$object = $this->mock(FilePrerequisites::class); $job = new ImportJob;
$object->shouldReceive('setUser'); $job->user_id = $this->user()->id;
$object->shouldReceive('hasPrerequisites')->andReturn(true); $job->key = 'A_pre_job_' . random_int(1, 1000);
$object->shouldReceive('getView')->andReturn('error'); // does not matter which view is returned $job->status = 'new';
$object->shouldReceive('getViewParameters')->andReturn([]); $job->provider = 'fake';
$this->be($this->user()); $job->transactions = [];
$job->file_type = '';
$job->save();
$response = $this->get(route('import.prerequisites', ['file'])); // mock stuff
$prereq = $this->mock(FakePrerequisites::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$prereq->shouldReceive('setUser')->once();
$prereq->shouldReceive('isComplete')->once()->andReturn(false);
$prereq->shouldReceive('getView')->once()->andReturn('import.fake.prerequisites');
$prereq->shouldReceive('getViewParameters')->once()->andReturn(['api_key' => '']);
$this->be($this->user());
$response = $this->get(route('import.prerequisites.index', ['fake', $job->key]));
$response->assertStatus(200); $response->assertStatus(200);
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController::index * @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
*/ */
public function testIndexRedirect() public function testIndexBadState(): void
{ {
$object = $this->mock(FilePrerequisites::class); $job = new ImportJob;
$object->shouldReceive('setUser'); $job->user_id = $this->user()->id;
$object->shouldReceive('hasPrerequisites')->andReturn(false); $job->key = 'B_pre_job_' . random_int(1, 1000);
$this->be($this->user()); $job->status = 'some_Bad_state';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
$response = $this->get(route('import.prerequisites', ['file'])); $this->be($this->user());
$response = $this->get(route('import.prerequisites.index', ['fake', $job->key]));
$response->assertStatus(302); $response->assertStatus(302);
$response->assertRedirect(route('import.create-job', ['file'])); $response->assertRedirect(route('import.index'));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
*/
public function testIndexComplete(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'C_pre_job_' . random_int(1, 1000);
$job->status = 'new';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$prereq = $this->mock(FakePrerequisites::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'has_prereq']);
$prereq->shouldReceive('setUser')->once();
$prereq->shouldReceive('isComplete')->once()->andReturn(true);
$this->be($this->user());
$response = $this->get(route('import.prerequisites.index', ['fake', $job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.job.configuration.index', [$job->key]));
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController::post * Redirects to configuration.
*
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
*/ */
public function testPost() public function testPost(): void
{ {
$messageBag = new MessageBag; $job = new ImportJob;
$messageBag->add('nomessage', 'nothing'); $job->user_id = $this->user()->id;
$object = $this->mock(FilePrerequisites::class); $job->key = 'D_pre_job_' . random_int(1, 1000);
$object->shouldReceive('setUser'); $job->status = 'new';
$object->shouldReceive('hasPrerequisites')->andReturn(true); $job->provider = 'fake';
$object->shouldReceive('storePrerequisites')->andReturn($messageBag); $job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$prereq = $this->mock(FakePrerequisites::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$prereq->shouldReceive('setUser')->once();
$prereq->shouldReceive('storePrerequisites')->once()->andReturn(new MessageBag);
$repository->shouldReceive('setStatus')->once()->withArgs([Mockery::any(), 'has_prereq']);
$this->be($this->user()); $this->be($this->user());
$response = $this->post(route('import.prerequisites.post', ['fake', $job->key]));
$response = $this->post(route('import.prerequisites.post', ['file']), []);
$response->assertStatus(302); $response->assertStatus(302);
$response->assertSessionHas('error'); $response->assertRedirect(route('import.job.configuration.index', [$job->key]));
$response->assertRedirect(route('import.prerequisites', ['file']));
} }
/** /**
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController::post * Bad state gives errors.
*
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
*/ */
public function testPostDone() public function testPostBadState(): void
{ {
$messageBag = new MessageBag; $job = new ImportJob;
$messageBag->add('nomessage', 'nothing'); $job->user_id = $this->user()->id;
$object = $this->mock(FilePrerequisites::class); $job->key = 'D_pre_job_' . random_int(1, 1000);
$object->shouldReceive('setUser'); $job->status = 'badstate';
$object->shouldReceive('hasPrerequisites')->andReturn(false); $job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
// mock stuff
$prereq = $this->mock(FakePrerequisites::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$this->be($this->user()); $this->be($this->user());
$response = $this->post(route('import.prerequisites.post', ['fake', $job->key]));
$response = $this->post(route('import.prerequisites.post', ['file']), []);
$response->assertStatus(302); $response->assertStatus(302);
$response->assertRedirect(route('import.create-job', ['file'])); $response->assertRedirect(route('import.index'));
$response->assertSessionHas('error', 'To access this page, your import job cannot have status "badstate".');
}
/**
* Redirects to index.
*
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
*/
public function testPostNoJob(): void
{
// mock stuff
$prereq = $this->mock(FakePrerequisites::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$prereq->shouldReceive('setUser')->once();
$prereq->shouldReceive('storePrerequisites')->once()->andReturn(new MessageBag);
$this->be($this->user());
$response = $this->post(route('import.prerequisites.post', ['fake']));
$response->assertStatus(302);
$response->assertRedirect(route('import.index'));
}
/**
* Error messages? Redirect back
*
* @covers \FireflyIII\Http\Controllers\Import\PrerequisitesController
*/
public function testPostWithMessages(): void
{
$job = new ImportJob;
$job->user_id = $this->user()->id;
$job->key = 'D_pre_job_' . random_int(1, 1000);
$job->status = 'new';
$job->provider = 'fake';
$job->transactions = [];
$job->file_type = '';
$job->save();
$messages = new MessageBag;
$messages->add('some', 'message');
// mock stuff
$prereq = $this->mock(FakePrerequisites::class);
$repository = $this->mock(ImportJobRepositoryInterface::class);
$prereq->shouldReceive('setUser')->once();
$prereq->shouldReceive('storePrerequisites')->once()->andReturn($messages);
$this->be($this->user());
$response = $this->post(route('import.prerequisites.post', ['fake', $job->key]));
$response->assertStatus(302);
$response->assertRedirect(route('import.prerequisites.index', ['fake', $job->key]));
$response->assertSessionHas('error', 'message');
} }
} }

View File

@ -1,96 +0,0 @@
<?php
/**
* StatusControllerTest.php
* Copyright (c) 2017 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\Feature\Controllers\Import;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Log;
use Tests\TestCase;
/**
* Class AccountControllerTest
*
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
*/
class StatusControllerTest extends TestCase
{
/**
*
*/
public function setUp()
{
parent::setUp();
Log::debug(sprintf('Now in %s.', \get_class($this)));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\StatusController::__construct
* @covers \FireflyIII\Http\Controllers\Import\StatusController::index
*/
public function testIndex()
{
$this->be($this->user());
$response = $this->get(route('import.status', ['configured']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\StatusController::__construct
* @covers \FireflyIII\Http\Controllers\Import\StatusController::index
*/
public function testIndexRedirect()
{
$this->be($this->user());
$response = $this->get(route('import.status', ['new']));
$response->assertStatus(302);
$response->assertRedirect(route('import.configure', ['new']));
}
/**
* @covers \FireflyIII\Http\Controllers\Import\StatusController::__construct
* @covers \FireflyIII\Http\Controllers\Import\StatusController::json
*/
public function testStatusFinished()
{
$tag = $this->user()->tags()->first();
$repository = $this->mock(TagRepositoryInterface::class);
$repository->shouldReceive('find')->andReturn($tag);
$this->be($this->user());
$response = $this->get(route('import.status.json', ['finished']));
$response->assertStatus(200);
}
/**
* @covers \FireflyIII\Http\Controllers\Import\StatusController::__construct
* @covers \FireflyIII\Http\Controllers\Import\StatusController::json
*/
public function testStatusRunning()
{
$this->be($this->user());
$response = $this->get(route('import.status.json', ['running']));
$response->assertStatus(200);
}
}