diff --git a/app/Http/Controllers/Import/ConfigurationController.php b/app/Http/Controllers/Import/ConfigurationController.php index 15a6244c..b7513899 100644 --- a/app/Http/Controllers/Import/ConfigurationController.php +++ b/app/Http/Controllers/Import/ConfigurationController.php @@ -146,6 +146,7 @@ class ConfigurationController extends Controller // store config on drive. $fromRequest = $request->getAll(); $configuration = Configuration::fromRequest($fromRequest); + $configuration->setFlow($request->cookie('flow')); $json = '[]'; try { diff --git a/app/Http/Controllers/Import/CSV/ConvertController.php b/app/Http/Controllers/Import/ConversionController.php similarity index 52% rename from app/Http/Controllers/Import/CSV/ConvertController.php rename to app/Http/Controllers/Import/ConversionController.php index 7f2b0c78..7fc29a1a 100644 --- a/app/Http/Controllers/Import/CSV/ConvertController.php +++ b/app/Http/Controllers/Import/ConversionController.php @@ -22,7 +22,7 @@ declare(strict_types=1); -namespace App\Http\Controllers\Import\CSV; +namespace App\Http\Controllers\Import; use App\Exceptions\ImporterErrorException; @@ -30,24 +30,24 @@ use App\Http\Controllers\Controller; use App\Http\Middleware\ReadyForImport; use App\Mail\ImportFinished; use App\Services\CSV\Configuration\Configuration; -use App\Services\CSV\File\FileReader; +use App\Services\CSV\Conversion\RoutineManager as CSVRoutineManager; use App\Services\Import\ImportJobStatus\ImportJobStatus; use App\Services\Import\ImportJobStatus\ImportJobStatusManager; -use App\Services\Import\ImportRoutineManager; use App\Services\Session\Constants; +use App\Services\Shared\Conversion\ConversionStatus; +use App\Services\Shared\Conversion\RoutineManagerInterface; +use App\Services\Shared\Conversion\RoutineStatusManager; use App\Services\Storage\StorageService; -use ErrorException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use JsonException; use Log; use Mail; -use TypeError; /** - * Class ConvertController + * Class ConversionController */ -class ConvertController extends Controller +class ConversionController extends Controller { /** @@ -67,7 +67,6 @@ class ConvertController extends Controller { Log::debug(sprintf('Now in %s', __METHOD__)); $mainTitle = 'Import the data'; - $subTitle = 'Connect to Firefly III and store your data'; // get configuration object. $configuration = Configuration::fromArray(session()->get(Constants::CONFIGURATION)); @@ -77,7 +76,6 @@ class ConvertController extends Controller if (null !== $configFileName) { $diskArray = json_decode(StorageService::getContent(session()->get($configFileName)), true, JSON_THROW_ON_ERROR); $diskConfig = Configuration::fromArray($diskArray); - $configuration->setDoMapping($diskConfig->getDoMapping()); $configuration->setMapping($diskConfig->getMapping()); } @@ -96,80 +94,113 @@ class ConvertController extends Controller } // job ID may be in session: - $identifier = session()->get(Constants::CSV_CONVERSION_JOB_IDENTIFIER); - $routine = new ImportRoutineManager($identifier); + $identifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER); + $flow = $configuration->getFlow(); + + // switch based on flow: + if (!in_array($flow, config('importer.flows'), true)) { + throw new ImporterErrorException(sprintf('Not a supported flow: "%s"', $flow)); + } + /** @var RoutineManagerInterface $routine */ + if ('csv' === $flow) { + $routine = new CSVRoutineManager(null); + } + if ('nordigen' === $flow) { + throw new ImporterErrorException('Cannot handle. :('); + } + if ('spectre' === $flow) { + throw new ImporterErrorException('Cannot handle. :('); + } + + // may be a new identifier! Yay! $identifier = $routine->getIdentifier(); - Log::debug(sprintf('Import routine manager identifier is "%s"', $identifier)); + Log::debug(sprintf('Conversion routine manager identifier is "%s"', $identifier)); // store identifier in session so the status can get it. - session()->put(Constants::CSV_CONVERSION_JOB_IDENTIFIER, $identifier); - Log::debug(sprintf('Stored "%s" under "%s"', $identifier, Constants::CSV_CONVERSION_JOB_IDENTIFIER)); + session()->put(Constants::CONVERSION_JOB_IDENTIFIER, $identifier); + Log::debug(sprintf('Stored "%s" under "%s"', $identifier, Constants::CONVERSION_JOB_IDENTIFIER)); - return view('import.007-convert.index', compact('mainTitle', 'subTitle', 'identifier', 'jobBackUrl')); + return view('import.007-convert.index', compact('mainTitle', 'identifier', 'jobBackUrl')); } /** * @param Request $request * * @return JsonResponse - * @throws JsonException - * @throws JsonException */ public function start(Request $request): JsonResponse { Log::debug(sprintf('Now at %s', __METHOD__)); $identifier = $request->get('identifier'); - $routine = new ImportRoutineManager($identifier); - $importJobStatus = ImportJobStatusManager::startOrFindJob($identifier); - ImportJobStatusManager::setJobStatus(ImportJobStatus::JOB_RUNNING); + // start new conversion routine, depending on the type of import: + // read configuration from session + $configuration = Configuration::fromArray(session()->get(Constants::CONFIGURATION)); - try { - // read configuration from session - $configuration = Configuration::fromArray(session()->get(Constants::CONFIGURATION)); - - // read configuration from disk (to append data) - $configurationFile = session()->get(Constants::UPLOAD_CONFIG_FILE); - if (null !== $configurationFile) { - $diskArray = json_decode(StorageService::getContent($configurationFile), true, JSON_THROW_ON_ERROR); - $diskConfig = Configuration::fromArray($diskArray); - $configuration->setMapping($diskConfig->getMapping()); - $configuration->setDoMapping($diskConfig->getDoMapping()); - $configuration->setRoles($diskConfig->getRoles()); - } + // read configuration from disk (to append data) + $configurationFile = session()->get(Constants::UPLOAD_CONFIG_FILE); + if (null !== $configurationFile) { + $diskArray = json_decode(StorageService::getContent($configurationFile), true, JSON_THROW_ON_ERROR); + $diskConfig = Configuration::fromArray($diskArray); + $configuration->setMapping($diskConfig->getMapping()); + $configuration->setDoMapping($diskConfig->getDoMapping()); + $configuration->setRoles($diskConfig->getRoles()); + } - $routine->setConfiguration($configuration); - $routine->setReader(FileReader::getReaderFromSession()); - $routine->start(); - } /** @noinspection PhpRedundantCatchClauseInspection */ catch (ImporterErrorException | ErrorException | TypeError $e) { - // update job to error state. - ImportJobStatusManager::setJobStatus(ImportJobStatus::JOB_ERRORED); - $error = sprintf('Internal error: %s in file %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()); - Log::error($e->getMessage()); - Log::error($e->getTraceAsString()); - ImportJobStatusManager::addError($identifier, 0, $error); - - return response()->json($importJobStatus->toArray()); + // now create the right class: + $flow = $configuration->getFlow(); + if (!in_array($flow, config('importer.flows'), true)) { + throw new ImporterErrorException(sprintf('Not a supported flow: "%s"', $flow)); + } + /** @var RoutineManagerInterface $routine */ + if ('csv' === $flow) { + $routine = new CSVRoutineManager($identifier); + } + if ('nordigen' === $flow) { + throw new ImporterErrorException('Cannot handle. :('); + } + if ('spectre' === $flow) { + throw new ImporterErrorException('Cannot handle. :('); } - // set done: - ImportJobStatusManager::setJobStatus(ImportJobStatus::JOB_DONE); - - // if configured, send report! - $log - = [ - 'messages' => $routine->getAllMessages(), - 'warnings' => $routine->getAllWarnings(), - 'errors' => $routine->getAllErrors(), - ]; - - $send = config('mail.enable_mail_report'); - Log::debug('Log log', $log); - if (true === $send) { - Log::debug('SEND MAIL'); - Mail::to(config('mail.destination'))->send(new ImportFinished($log)); + $importJobStatus = RoutineStatusManager::startOrFindConversion($identifier); + + RoutineStatusManager::setConversionStatus(ConversionStatus::CONVERSION_RUNNING); + + // then push stuff into the routine: + $routine->setConfiguration($configuration); + //$routine->setReader(FileReader::getReaderFromSession()); + $result = false; + try { + $routine->start(); + $result = true; + } catch (ImporterErrorException $e) { + RoutineStatusManager::setConversionStatus(ConversionStatus::CONVERSION_ERRORED); } + die('here we are'); + + if (true === $result) { + // set done: + RoutineStatusManager::setConversionStatus(ConversionStatus::CONVERSION_DONE); + + // if configured, send report! + // TODO make event handler. + $log + = [ + 'messages' => $routine->getAllMessages(), + 'warnings' => $routine->getAllWarnings(), + 'errors' => $routine->getAllErrors(), + ]; + + $send = config('mail.enable_mail_report'); + Log::debug('Log log', $log); + if (true === $send) { + Log::debug('SEND MAIL'); + Mail::to(config('mail.destination'))->send(new ImportFinished($log)); + } + } + return response()->json($importJobStatus->toArray()); } diff --git a/app/Services/CSV/Configuration/Configuration.php b/app/Services/CSV/Configuration/Configuration.php index 58d89e49..d8ccf562 100644 --- a/app/Services/CSV/Configuration/Configuration.php +++ b/app/Services/CSV/Configuration/Configuration.php @@ -34,7 +34,7 @@ use UnexpectedValueException; class Configuration { /** @var int */ - public const VERSION = 2; + public const VERSION = 3; private string $date; private int $defaultAccount; private string $delimiter; @@ -47,6 +47,9 @@ class Configuration private array $doMapping; private bool $addImportTag; + // what type of import? + private string $flow; + // how to do double transaction detection? private array $mapping; // 'classic' or 'cell' @@ -75,6 +78,7 @@ class Configuration $this->roles = []; $this->mapping = []; $this->doMapping = []; + $this->flow = 'csv'; // double transaction detection: $this->duplicateDetectionMethod = 'classic'; @@ -132,6 +136,9 @@ class Configuration $object->uniqueColumnIndex = $array['unique_column_index'] ?? 0; $object->uniqueColumnType = $array['unique_column_type'] ?? ''; + // flow + $object->flow = $array['flow'] ?? 'csv'; + // overrule a setting: if ('none' === $object->duplicateDetectionMethod) { $object->ignoreDuplicateTransactions = false; @@ -166,6 +173,12 @@ class Configuration return self::fromVersionTwo($data); } + if (3 === $version) { + Log::debug('v3 config file!'); + + return self::fromVersionThree($data); + } + throw new UnexpectedValueException(sprintf('Configuration file version "%s" cannot be parsed.', $version)); } @@ -184,6 +197,7 @@ class Configuration $object->delimiter = $delimiters[$data['delimiter']] ?? 'comma'; $object->defaultAccount = $data['import-account'] ?? $object->defaultAccount; $object->rules = $data['apply-rules'] ?? true; + $object->flow = $data['flow'] ?? 'csv'; $object->ignoreDuplicateTransactions = $data['ignore_duplicate_transactions'] ?? true; @@ -262,6 +276,18 @@ class Configuration return self::fromArray($data); } + /** + * @param array $data + * + * @return static + */ + private static function fromVersionThree(array $data): self + { + $object = self::fromArray($data); + $object->specifics = []; + return $object; + } + /** * @param array $array * @@ -279,11 +305,11 @@ class Configuration $object->rules = $array['rules']; $object->skipForm = $array['skip_form']; $object->addImportTag = $array['add_import_tag'] ?? true; - $object->specifics = $array['specifics']; $object->roles = $array['roles'] ?? []; $object->mapping = $array['mapping'] ?? []; $object->doMapping = $array['do_mapping'] ?? []; $object->version = $version; + $object->flow = $array['flow'] ?? 'csv'; // duplicate transaction detection $object->duplicateDetectionMethod = $array['duplicate_detection_method'] ?? 'classic'; @@ -308,24 +334,6 @@ class Configuration $object->uniqueColumnIndex = $array['unique_column_index'] ?? 0; $object->uniqueColumnType = $array['unique_column_type'] ?? ''; - $firstValue = count(array_values($array['specifics'])) > 0 ? array_values($array['specifics'])[0] : null; - $firstKey = count(array_values($array['specifics'])) > 0 ? array_keys($array['specifics'])[0] : null; - - // due to a bug, the "specifics" array could still be broken at this point. - // do a quick check and verification. - if (is_bool($firstValue) && is_string($firstKey)) { - $actualSpecifics = []; - foreach ($array['specifics'] as $key => $value) { - if (true === $value) { - $actualSpecifics[] = $key; - } - } - $object->specifics = $actualSpecifics; - } - - //Log::debug(var_export($object->ignoreDuplicateLines, true)); - //Log::debug(var_export($object->ignoreDuplicateTransactions, true)); - return $object; } @@ -411,7 +419,6 @@ class Configuration 'rules' => $this->rules, 'skip_form' => $this->skipForm, 'add_import_tag' => $this->addImportTag, - 'specifics' => $this->specifics, 'roles' => $this->roles, 'do_mapping' => $this->doMapping, 'mapping' => $this->mapping, @@ -421,6 +428,7 @@ class Configuration 'unique_column_index' => $this->uniqueColumnIndex, 'unique_column_type' => $this->uniqueColumnType, 'version' => $this->version, + 'flow' => $this->flow, ]; // make sure that "ignore duplicate transactions" is turned off @@ -503,7 +511,7 @@ class Configuration public function setMapping(array $mapping): void { $newMap = []; - foreach($mapping as $column => $map) { + foreach ($mapping as $column => $map) { ksort($map); $newMap[$column] = $map; } @@ -542,5 +550,21 @@ class Configuration return $this->uniqueColumnType; } + /** + * @return string + */ + public function getFlow(): string + { + return $this->flow; + } + + /** + * @param string $flow + */ + public function setFlow(string $flow): void + { + $this->flow = $flow; + } + } diff --git a/app/Services/CSV/Conversion/RoutineManager.php b/app/Services/CSV/Conversion/RoutineManager.php new file mode 100644 index 00000000..e9113ba8 --- /dev/null +++ b/app/Services/CSV/Conversion/RoutineManager.php @@ -0,0 +1,126 @@ +. + */ + +namespace App\Services\CSV\Conversion; + +use App\Services\CSV\Configuration\Configuration; +use App\Services\Import\Routine\CSVFileProcessor; +use App\Services\Shared\Conversion\RoutineManagerInterface; +use Log; +use Storage; +use Str; + +/** + * Class RoutineManager + */ +class RoutineManager implements RoutineManagerInterface +{ + private const DISK_NAME = 'conversion-routines'; + + private Configuration $configuration; + private string $identifier; + private CSVFileProcessor $csvFileProcessor; + + /** + * + */ + public function __construct(?string $identifier) + { + if (null === $identifier) { + $this->generateIdentifier(); + } + if (null !== $identifier) { + $this->identifier = $identifier; + } + } + + /** + * @inheritDoc + */ + public function setConfiguration(Configuration $configuration): void + { + // save config + $this->configuration = $configuration; + + // share config + $this->csvFileProcessor = new CSVFileProcessor($this->configuration); + + // set identifier: + $this->csvFileProcessor->setIdentifier($this->identifier); + } + + /** + * @inheritDoc + */ + public function start(): void + { + Log::debug(sprintf('Now in %s', __METHOD__)); + + // convert CSV file into raw lines (arrays) + $this->csvFileProcessor->setSpecifics($this->configuration->getSpecifics()); + $this->csvFileProcessor->setHasHeaders($this->configuration->isHeaders()); + $this->csvFileProcessor->setDelimiter($this->configuration->getDelimiter()); + $CSVLines = $this->csvFileProcessor->processCSVFile(); + + // convert raw lines into arrays with individual ColumnValues + $valueArrays = $this->lineProcessor->processCSVLines($CSVLines); + + // convert value arrays into (pseudo) transactions. + $pseudo = $this->columnValueConverter->processValueArrays($valueArrays); + + // convert pseudo transactions into actual transactions. + $transactions = $this->pseudoTransactionProcessor->processPseudo($pseudo); + + // save transactions to disk + die('TODO save to disk!'); + + $count = count($CSVLines); + $this->mergeMessages($count); + $this->mergeWarnings($count); + $this->mergeErrors($count); + } + + /** + * @inheritDoc + */ + public function getIdentifier(): string + { + return $this->identifier; + } + + /** + * + */ + private function generateIdentifier(): void + { + Log::debug('Going to generate conversion routine identifier.'); + $disk = Storage::disk(self::DISK_NAME); + $count = 0; + do { + $generatedId = sprintf('conv-%s', Str::random(12)); + $count++; + Log::debug(sprintf('Attempt #%d results in "%s"', $count, $generatedId)); + } while ($count < 30 && $disk->exists($generatedId)); + $this->identifier = $generatedId; + Log::info(sprintf('Job identifier is "%s"', $generatedId)); + } +} diff --git a/app/Services/Import/ImportJobStatus/ImportJobStatusManager.php b/app/Services/Import/ImportJobStatus/ImportJobStatusManager.php index ff0baa38..a4432d06 100644 --- a/app/Services/Import/ImportJobStatus/ImportJobStatusManager.php +++ b/app/Services/Import/ImportJobStatus/ImportJobStatusManager.php @@ -145,7 +145,7 @@ class ImportJobStatusManager */ public static function setJobStatus(string $status): ImportJobStatus { - $identifier = session()->get(Constants::CSV_CONVERSION_JOB_IDENTIFIER); + $identifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER); Log::debug(sprintf('Now in setJobStatus(%s)', $status)); Log::debug(sprintf('Found "%s" in the session', $identifier)); diff --git a/app/Services/Import/ImportRoutineManager.php b/app/Services/Import/ImportRoutineManager.php index 58eff702..6e14511a 100644 --- a/app/Services/Import/ImportRoutineManager.php +++ b/app/Services/Import/ImportRoutineManager.php @@ -76,6 +76,7 @@ class ImportRoutineManager */ public function __construct(string $identifier = null) { + die('i am deprecated'); Log::debug('Constructed ImportRoutineManager'); // get line converter diff --git a/app/Services/Session/Constants.php b/app/Services/Session/Constants.php index 10d5ba87..04ffd8fb 100644 --- a/app/Services/Session/Constants.php +++ b/app/Services/Session/Constants.php @@ -57,8 +57,8 @@ class Constants public const ROLES_COMPLETE_INDICATOR = 'role_config_complete'; public const MAPPING_COMPLETE_INDICATOR = 'mapping_config_complete'; - // constants for CSV conversion job: - public const CSV_CONVERSION_JOB_IDENTIFIER = 'csv_c_job_id'; + // constants for data conversion job: + public const CONVERSION_JOB_IDENTIFIER = 'conversion_job_id'; // /** @var string */ diff --git a/app/Services/Shared/Conversion/ConversionStatus.php b/app/Services/Shared/Conversion/ConversionStatus.php new file mode 100644 index 00000000..d6a01824 --- /dev/null +++ b/app/Services/Shared/Conversion/ConversionStatus.php @@ -0,0 +1,84 @@ +. + */ + +declare(strict_types=1); + +namespace App\Services\Shared\Conversion; + +/** + * Class ConversionStatus + */ +class ConversionStatus +{ + /** @var string */ + public const CONVERSION_DONE = 'conv_done'; + /** @var string */ + public const CONVERSION_ERRORED = 'conv_errored'; + /** @var string */ + public const CONVERSION_RUNNING = 'conv_running'; + /** @var string */ + public const CONVERSION_WAITING = 'waiting_to_start'; + public string $status; + public array $errors; + public array $warnings; + public array $messages; + + /** + * ConversionStatus constructor. + */ + public function __construct() + { + $this->status = self::CONVERSION_WAITING; + $this->errors = []; + $this->warnings = []; + $this->messages = []; + } + + /** + * @param array $array + * + * @return static + */ + public static function fromArray(array $array): self + { + $config = new self; + $config->status = $array['status']; + $config->errors = $array['errors'] ?? []; + $config->warnings = $array['warnings'] ?? []; + $config->messages = $array['messages'] ?? []; + + return $config; + } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'status' => $this->status, + 'errors' => $this->errors, + 'warnings' => $this->warnings, + 'messages' => $this->messages, + ]; + } +} diff --git a/app/Services/Shared/Conversion/RoutineManagerInterface.php b/app/Services/Shared/Conversion/RoutineManagerInterface.php new file mode 100644 index 00000000..e58288d7 --- /dev/null +++ b/app/Services/Shared/Conversion/RoutineManagerInterface.php @@ -0,0 +1,47 @@ +. + */ + +namespace App\Services\Shared\Conversion; + +use App\Services\CSV\Configuration\Configuration; + +/** + * Interface RoutineManagerInterface + */ +interface RoutineManagerInterface +{ + /** + * @param Configuration $configuration + */ + public function setConfiguration(Configuration $configuration): void; + + /** + * + */ + public function start(): void; + + /** + * @return string + */ + public function getIdentifier(): string; + +} diff --git a/app/Services/Shared/Conversion/RoutineStatusManager.php b/app/Services/Shared/Conversion/RoutineStatusManager.php new file mode 100644 index 00000000..fb05a71f --- /dev/null +++ b/app/Services/Shared/Conversion/RoutineStatusManager.php @@ -0,0 +1,205 @@ +. + */ + +declare(strict_types=1); + +namespace App\Services\Shared\Conversion; + +use App\Exceptions\ImporterErrorException; +use App\Services\Session\Constants; +use Illuminate\Contracts\Filesystem\FileNotFoundException; +use JsonException; +use Log; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; +use Storage; + +/** + * Class RoutineStatusManager + */ +class RoutineStatusManager +{ + private const DISK_NAME = 'conversion-routines'; + + /** + * @param string $identifier + * @param int $index + * @param string $error + */ + public static function addError(string $identifier, int $index, string $error): void + { + $lineNo = $index + 1; + Log::debug(sprintf('Add error on index #%d (line no. %d): %s', $index, $lineNo, $error)); + + $disk = Storage::disk(self::DISK_NAME); + try { + if ($disk->exists($identifier)) { + try { + $status = ConversionStatus::fromArray(json_decode($disk->get($identifier), true, 512, JSON_THROW_ON_ERROR)); + } catch (JsonException $e) { + Log::error($e->getMessage()); + $status = new ConversionStatus; + } + $status->errors[$index] = $status->errors[$index] ?? []; + $status->errors[$index][] = $error; + self::storeConversionStatus($identifier, $status); + } + } catch (FileNotFoundException $e) { + Log::error($e->getMessage()); + } + } + + /** + * @param string $identifier + * @param ConversionStatus $status + */ + private static function storeConversionStatus(string $identifier, ConversionStatus $status): void + { + Log::debug(sprintf('Now in storeConversionStatus(%s): %s', $identifier, $status->status)); + $disk = Storage::disk(self::DISK_NAME); + try { + $disk->put($identifier, json_encode($status->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT)); + } catch (JsonException $e) { + // do nothing + Log::error($e->getMessage()); + } + } + + /** + * @param string $identifier + * @param int $index + * @param string $warning + * + */ + public static function addWarning(string $identifier, int $index, string $warning): void + { + $lineNo = $index + 1; + Log::debug(sprintf('Add warning on index #%d (line no. %d): %s', $index, $lineNo, $warning)); + + $disk = Storage::disk(self::DISK_NAME); + try { + if ($disk->exists($identifier)) { + try { + $status = ConversionStatus::fromArray(json_decode($disk->get($identifier), true, 512, JSON_THROW_ON_ERROR)); + } catch (JsonException $e) { + Log::error($e->getMessage()); + $status = new ConversionStatus; + } + $status->warnings[$index] = $status->warnings[$index] ?? []; + $status->warnings[$index][] = $warning; + self::storeConversionStatus($identifier, $status); + } + } catch (FileNotFoundException $e) { + Log::error($e->getMessage()); + } + } + + /** + * @param string $identifier + * @param int $index + * @param string $message + * + */ + public static function addMessage(string $identifier, int $index, string $message): void + { + $lineNo = $index + 1; + Log::debug(sprintf('Add message on index #%d (line no. %d): %s', $index, $lineNo, $message)); + + $disk = Storage::disk(self::DISK_NAME); + try { + if ($disk->exists($identifier)) { + try { + $status = ConversionStatus::fromArray(json_decode($disk->get($identifier), true, 512, JSON_THROW_ON_ERROR)); + } catch (JsonException $e) { + Log::error($e->getMessage()); + $status = new ConversionStatus; + } + $status->messages[$index] = $status->messages[$index] ?? []; + $status->messages[$index][] = $message; + self::storeConversionStatus($identifier, $status); + } + } catch (FileNotFoundException $e) { + Log::error($e->getMessage()); + } + } + + + /** + * @param string $status + * + * @return ConversionStatus + * @throws ImporterErrorException + */ + public static function setConversionStatus(string $status): ConversionStatus + { + try { + $identifier = session()->get(Constants::CONVERSION_JOB_IDENTIFIER); + } catch (ContainerExceptionInterface|NotFoundExceptionInterface $e) { + throw new ImporterErrorException('No identifier found'); + } + Log::debug(sprintf('Now in setConversionStatus(%s)', $status)); + Log::debug(sprintf('Found "%s" in the session', $identifier)); + + $jobStatus = self::startOrFindConversion($identifier); + $jobStatus->status = $status; + + self::storeConversionStatus($identifier, $jobStatus); + + return $jobStatus; + } + + /** + * @param string $identifier + * + * @return ConversionStatus + */ + public static function startOrFindConversion(string $identifier): ConversionStatus + { + Log::debug(sprintf('Now in startOrFindConversion(%s)', $identifier)); + $disk = Storage::disk(self::DISK_NAME); + Log::debug(sprintf('Try to see if file exists for conversion "%s".', $identifier)); + if ($disk->exists($identifier)) { + Log::debug(sprintf('Status file exists for conversion "%s".', $identifier)); + try { + $array = json_decode($disk->get($identifier), true, 512, JSON_THROW_ON_ERROR); + $status = ConversionStatus::fromArray($array); + } catch (FileNotFoundException | JsonException $e) { + Log::error($e->getMessage()); + $status = new ConversionStatus; + } + + return $status; + + } + Log::debug('File does not exist or error, create a new one.'); + $status = new ConversionStatus; + try { + $disk->put($identifier, json_encode($status->toArray(), JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT)); + } catch (JsonException $e) { + Log::error($e->getMessage()); + } + + Log::debug('Return status.', $status->toArray()); + + return $status; + } +} diff --git a/config/filesystems.php b/config/filesystems.php index d6f48c0f..92066ca9 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -77,6 +77,10 @@ return [ 'driver' => 'local', 'root' => storage_path('jobs'), ], + 'conversion-routines' => [ + 'driver' => 'local', + 'root' => storage_path('conversion-routines'), + ], 'configurations' => [ 'driver' => 'local', 'root' => envNonEmpty('JSON_CONFIGURATION_DIR', storage_path('configurations')), diff --git a/resources/views/index.twig b/resources/views/index.twig index 1960fb6f..3853d3fb 100644 --- a/resources/views/index.twig +++ b/resources/views/index.twig @@ -96,7 +96,7 @@ Import from Nordigen (img)
-

+
@@ -107,7 +107,7 @@ Import from Spectre
-

+
diff --git a/routes/web.php b/routes/web.php index d6507b15..e1384996 100644 --- a/routes/web.php +++ b/routes/web.php @@ -61,10 +61,10 @@ Route::post('/import/roles', ['uses' => 'Import\CSV\RoleController@postIndex', ' Route::get('/import/mapping', ['uses' => 'Import\CSV\MapController@index', 'as' => '006-mapping.index']); Route::post('/import/mapping', ['uses' => 'Import\CSV\MapController@postIndex', 'as' => '006-mapping.post']); -// step 7: convert CSV to JSON transactions (CSV) -Route::get('/import/convert', ['uses' => 'Import\CSV\ConvertController@index', 'as' => '007-convert.index']); -Route::post('/import/convert/start', ['uses' => 'Import\CSV\ConvertController@start', 'as' => '007-convert.start']); -Route::get('/import/convert/status', ['uses' => 'Import\CSV\ConvertController@status', 'as' => '007-convert.status']); +// step 7: convert any import to JSON transactions +Route::get('/import/convert', ['uses' => 'Import\ConversionController@index', 'as' => '007-convert.index']); +Route::any('/import/convert/start', ['uses' => 'Import\ConversionController@start', 'as' => '007-convert.start']); +Route::get('/import/convert/status', ['uses' => 'Import\ConversionController@status', 'as' => '007-convert.status']); // routes to go back to other steps (also takes care of session vars) diff --git a/storage/conversion-routines/.gitignore b/storage/conversion-routines/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/storage/conversion-routines/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore