. */ declare(strict_types=1); namespace FireflyIII\Http\Controllers\Import; use Exception; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Import\Routine\RoutineInterface; use FireflyIII\Import\Storage\ImportArrayStorage; use FireflyIII\Models\ImportJob; use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface; use Illuminate\Http\JsonResponse; use Log; /** * Class JobStatusController */ class JobStatusController extends Controller { /** @var ImportJobRepositoryInterface */ private $repository; /** * */ public function __construct() { parent::__construct(); $this->middleware( function ($request, $next) { app('view')->share('mainTitleIcon', 'fa-archive'); app('view')->share('title', trans('firefly.import_index_title')); $this->repository = app(ImportJobRepositoryInterface::class); return $next($request); } ); } /** * @param ImportJob $importJob * * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ public function index(ImportJob $importJob) { $subTitleIcon = 'fa-gear'; $subTitle = trans('import.job_status_breadcrumb', ['key' => $importJob->key]); return view('import.status', compact('importJob', 'subTitle', 'subTitleIcon')); } /** * @param ImportJob $importJob * * @return JsonResponse */ public function json(ImportJob $importJob): JsonResponse { $count = \count($importJob->transactions); $json = [ 'status' => $importJob->status, 'errors' => $importJob->errors, 'count' => $count, 'tag_id' => $importJob->tag_id, 'tag_name' => null === $importJob->tag_id ? null : $importJob->tag->tag, 'report_txt' => trans('import.unknown_import_result'), 'download_config' => false, 'download_config_text' => '', ]; if ($importJob->provider === 'file') { $json['download_config'] = true; $json['download_config_text'] = trans('import.should_download_config', ['route' => route('import.job.download', [$importJob->key])]) . ' ' . trans('import.share_config_file'); } // if count is zero: if (null !== $importJob->tag_id) { $count = $importJob->tag->transactionJournals->count(); } if ($count === 0) { $json['report_txt'] = trans('import.result_no_transactions'); } if ($count === 1 && null !== $importJob->tag_id) { $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) { $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); } /** * @param ImportJob $importJob * * @return JsonResponse */ public function start(ImportJob $importJob): JsonResponse { // catch impossible status: $allowed = ['ready_to_run', 'need_job_config', 'error']; // todo remove error if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { Log::error('Job is not ready.'); $this->repository->setStatus($importJob, 'error'); return response()->json( ['status' => 'NOK', 'message' => sprintf('JobStatusController::start expects status "ready_to_run" instead of "%s".', $importJob->status)] ); } $importProvider = $importJob->provider; $key = sprintf('import.routine.%s', $importProvider); $className = config($key); if (null === $className || !class_exists($className)) { // @codeCoverageIgnoreStart return response()->json( ['status' => 'NOK', 'message' => sprintf('Cannot find import routine class for job of type "%s".', $importProvider)] ); // @codeCoverageIgnoreEnd } /** @var RoutineInterface $routine */ $routine = app($className); $routine->setImportJob($importJob); try { $routine->run(); } catch (FireflyException|Exception $e) { $message = 'The import 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]); } // expect nothing from routine, just return OK to user. return response()->json(['status' => 'OK', 'message' => 'stage_finished']); } /** * Store does three things: * * - Store the transactions. * - Add them to a tag. * * @param ImportJob $importJob * * @return JsonResponse */ public function store(ImportJob $importJob): JsonResponse { // catch impossible status: $allowed = ['provider_finished', 'storing_data']; if (null !== $importJob && !\in_array($importJob->status, $allowed, true)) { Log::error('Job is not ready.'); return response()->json( ['status' => 'NOK', 'message' => sprintf('JobStatusController::start expects status "provider_finished" instead of "%s".', $importJob->status)] ); } // set job to be storing data: $this->repository->setStatus($importJob, 'storing_data'); try { $this->storeTransactions($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 storage to be finished: $this->repository->setStatus($importJob, 'storage_finished'); // expect nothing from routine, just return OK to user. return response()->json(['status' => 'OK', 'message' => 'storage_finished']); } /** * @param ImportJob $importJob * * @throws FireflyException */ private function storeTransactions(ImportJob $importJob): void { /** @var ImportArrayStorage $storage */ $storage = app(ImportArrayStorage::class); $storage->setImportJob($importJob); try { $storage->store(); } catch (FireflyException|Exception $e) { throw new FireflyException($e->getMessage()); } } }