diff --git a/app/Import/Routine/SpectreRoutine.php b/app/Import/Routine/SpectreRoutine.php index fd9f0c6e03..fbd12ae9c7 100644 --- a/app/Import/Routine/SpectreRoutine.php +++ b/app/Import/Routine/SpectreRoutine.php @@ -22,11 +22,11 @@ declare(strict_types=1); namespace FireflyIII\Import\Routine; +use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\ImportJob; -use FireflyIII\Models\SpectreProvider; use FireflyIII\Services\Spectre\Object\Customer; -use FireflyIII\Services\Spectre\Request\CreateLoginRequest; -use FireflyIII\Services\Spectre\Request\ListLoginsRequest; +use FireflyIII\Services\Spectre\Object\Token; +use FireflyIII\Services\Spectre\Request\CreateTokenRequest; use FireflyIII\Services\Spectre\Request\NewCustomerRequest; use Illuminate\Support\Collection; use Log; @@ -90,31 +90,48 @@ class SpectreRoutine implements RoutineInterface return false; } - set_time_limit(0); Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key)); - // create customer if user does not have one: - $customer = $this->getCustomer(); + set_time_limit(0); - // list all logins present at Spectre - $logins = $this->listLogins($customer); + // check if job has token first! + $config = $this->job->configuration; + $hasToken = $config['has-token'] ?? false; + if ($hasToken === false) { + Log::debug('Job has no token'); + // create customer if user does not have one: + $customer = $this->getCustomer(); + Log::debug(sprintf('Customer ID is %s', $customer->getId())); + // use customer to request a token: + $uri = route('import.status', [$this->job->key]); + $token = $this->getToken($customer, $uri); + Log::debug(sprintf('Token is %s', $token->getToken())); - // use latest (depending on status, and if login exists for selected country + provider) - $country = $this->job->configuration['country']; - $providerId = $this->job->configuration['provider']; - $login = $this->filterLogins($logins, $country, $providerId); + // update job, give it the token: + $config = $this->job->configuration; + $config['has-token'] = true; + $config['token'] = $token->getToken(); + $config['token-expires'] = $token->getExpiresAt()->format('U'); + $config['token-url'] = $token->getConnectUrl(); + $this->job->configuration = $config; - // create new login if list is empty or no login exists. - if (is_null($login)) { - $login = $this->createLogin($customer); - var_dump($login); - exit; + Log::debug('Job config is now', $config); + + // update job, set status to "configuring". + $this->job->status = 'configuring'; + $this->job->save(); + Log::debug(sprintf('Job status is now %s', $this->job->status)); + + return true; + } + $isRedirected = $config['is-redirected'] ?? false; + if ($isRedirected === true) { + // assume user has "used" the token. + // ... + // now what? + throw new FireflyException('Application cannot handle this.'); } - echo '
'; - print_r($logins); - exit; - - return true; + throw new FireflyException('Application cannot handle this.'); } /** @@ -135,49 +152,12 @@ class SpectreRoutine implements RoutineInterface $newCustomerRequest->call(); $customer = $newCustomerRequest->getCustomer(); - // store customer. Not sure where. User preference? TODO + Preferences::setForUser($this->job->user, 'spectre_customer', $customer->toArray()); + return $customer; } - /** - * @param Customer $customer - */ - protected function createLogin(Customer $customer) - { - - $providerId = intval($this->job->configuration['provider']); - $provider = $this->findProvider($providerId); - - - $createLoginRequest = new CreateLoginRequest($this->job->user); - $createLoginRequest->setCustomer($customer); - $createLoginRequest->setProvider($provider); - $createLoginRequest->setMandatoryFields($this->decrypt($this->job->configuration['mandatory-fields'])); - $createLoginRequest->call(); - echo '123'; - // country code, provider code (find by spectre ID) - // credentials - // daily_refresh=true - // fetch_type=recent - // include_fake_providers=true - // store_credentials=true - - - var_dump($this->job->configuration); - exit; - } - - /** - * @param int $providerId - * - * @return SpectreProvider|null - */ - protected function findProvider(int $providerId): ?SpectreProvider - { - return SpectreProvider::where('spectre_id', $providerId)->first(); - } - /** * @return Customer * @throws \FireflyIII\Exceptions\FireflyException @@ -188,61 +168,27 @@ class SpectreRoutine implements RoutineInterface if (is_null($preference)) { return $this->createCustomer(); } - var_dump($preference->data); - exit; + $customer = new Customer($preference->data); + + return $customer; } /** * @param Customer $customer + * @param string $returnUri * - * @return array + * @return Token * @throws \FireflyIII\Exceptions\FireflyException */ - protected function listLogins(Customer $customer): array + protected function getToken(Customer $customer, string $returnUri): Token { - $listLoginRequest = new ListLoginsRequest($this->job->user); - $listLoginRequest->setCustomer($customer); - $listLoginRequest->call(); + $request = new CreateTokenRequest($this->job->user); + $request->setUri($returnUri); + $request->setCustomer($customer); + $request->call(); + Log::debug('Call to get token is finished'); - $logins = $listLoginRequest->getLogins(); + return $request->getToken(); - return $logins; - } - - /** - * @param array $configuration - * - * @return array - */ - private function decrypt(array $configuration): array - { - $new = []; - foreach ($configuration as $key => $value) { - $new[$key] = app('steam')->tryDecrypt($value); - } - - return $new; - } - - /** - * Return login belonging to country and provider - * TODO must return Login object, not array - * - * @param array $logins - * @param string $country - * @param int $providerId - * - * @return array|null - */ - private function filterLogins(array $logins, string $country, int $providerId): ?array - { - if (count($logins) === 0) { - return null; - } - foreach ($logins as $login) { - die('do some filter'); - } - - return null; } } diff --git a/app/Jobs/GetSpectreProviders.php b/app/Jobs/GetSpectreProviders.php deleted file mode 100644 index d29da378fd..0000000000 --- a/app/Jobs/GetSpectreProviders.php +++ /dev/null @@ -1,109 +0,0 @@ -. - */ - -namespace FireflyIII\Jobs; - -use FireflyIII\Models\Configuration; -use FireflyIII\Models\SpectreProvider; -use FireflyIII\Services\Spectre\Request\ListProvidersRequest; -use FireflyIII\User; -use Illuminate\Bus\Queueable; -use Illuminate\Contracts\Queue\ShouldQueue; -use Illuminate\Foundation\Bus\Dispatchable; -use Illuminate\Queue\InteractsWithQueue; -use Illuminate\Queue\SerializesModels; -use Log; - -/** - * Class GetSpectreProviders - */ -class GetSpectreProviders implements ShouldQueue -{ - use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; - - /** - * @var User - */ - protected $user; - - /** - * Create a new job instance. - * - * @param User $user - */ - public function __construct(User $user) - { - $this->user = $user; - Log::debug('Constructed job GetSpectreProviders'); - } - - /** - * Execute the job. - * - * @throws \Illuminate\Container\EntryNotFoundException - * @throws \Exception - */ - public function handle() - { - /** @var Configuration $configValue */ - $configValue = app('fireflyconfig')->get('spectre_provider_download', 0); - $now = time(); - if ($now - intval($configValue->data) < 86400) { - Log::debug(sprintf('Difference is %d, so will NOT execute job.', ($now - intval($configValue->data)))); - - return; - } - Log::debug(sprintf('Difference is %d, so will execute job.', ($now - intval($configValue->data)))); - - // get user - - // fire away! - $request = new ListProvidersRequest($this->user); - $request->call(); - - // store all providers: - $providers = $request->getProviders(); - foreach ($providers as $provider) { - // find provider? - $dbProvider = SpectreProvider::where('spectre_id', $provider['id'])->first(); - if (is_null($dbProvider)) { - $dbProvider = new SpectreProvider; - } - // update fields: - $dbProvider->spectre_id = $provider['id']; - $dbProvider->code = $provider['code']; - $dbProvider->mode = $provider['mode']; - $dbProvider->status = $provider['status']; - $dbProvider->interactive = 1 === $provider['interactive']; - $dbProvider->automatic_fetch = 1 === $provider['automatic_fetch']; - $dbProvider->country_code = $provider['country_code']; - $dbProvider->data = $provider; - $dbProvider->save(); - Log::debug(sprintf('Stored provider #%d under ID #%d', $provider['id'], $dbProvider->id)); - } - - app('fireflyconfig')->set('spectre_provider_download', time()); - - return; - } -} diff --git a/app/Models/ImportJob.php b/app/Models/ImportJob.php index b9256ea25a..57015f946b 100644 --- a/app/Models/ImportJob.php +++ b/app/Models/ImportJob.php @@ -127,6 +127,7 @@ class ImportJob extends Model public function change(string $status): void { if (in_array($status, $this->validStatus)) { + Log::debug(sprintf('Job status set (in model) to "%s"', $status)); $this->status = $status; $this->save(); @@ -169,6 +170,7 @@ class ImportJob extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setConfigurationAttribute($value) @@ -178,6 +180,7 @@ class ImportJob extends Model /** * @codeCoverageIgnore + * * @param $value */ public function setExtendedStatusAttribute($value) diff --git a/app/Models/SpectreProvider.php b/app/Models/SpectreProvider.php deleted file mode 100644 index a1d056f93c..0000000000 --- a/app/Models/SpectreProvider.php +++ /dev/null @@ -1,52 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Models; - -use Illuminate\Database\Eloquent\Model; - -/** - * Class SpectreProvider - */ -class SpectreProvider extends Model -{ - /** - * The attributes that should be casted to native types. - * - * @var array - */ - protected $casts - = [ - 'spectre_id' => 'int', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'interactive' => 'boolean', - 'automatic_fetch' => 'boolean', - 'data' => 'array', - ]; - - /** - * @var array - */ - protected $fillable = ['spectre_id', 'code', 'mode', 'name', 'status', 'interactive', 'automatic_fetch', 'country_code', 'data']; -} diff --git a/app/Services/Spectre/Object/Customer.php b/app/Services/Spectre/Object/Customer.php index 53d046a6d9..755dcfc38d 100644 --- a/app/Services/Spectre/Object/Customer.php +++ b/app/Services/Spectre/Object/Customer.php @@ -93,4 +93,16 @@ class Customer extends SpectreObject { $this->secret = $secret; } + + /** + * @return array + */ + public function toArray(): array + { + return [ + 'id' => $this->id, + 'identifier' => $this->identifier, + 'secret' => $this->secret, + ]; + } } diff --git a/app/Services/Spectre/Object/Login.php b/app/Services/Spectre/Object/Login.php deleted file mode 100644 index 6de8bed874..0000000000 --- a/app/Services/Spectre/Object/Login.php +++ /dev/null @@ -1,32 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Services\Spectre\Object; - -/** - * Class Login - */ -class Login extends SpectreObject -{ - -} \ No newline at end of file diff --git a/app/Services/Spectre/Object/Token.php b/app/Services/Spectre/Object/Token.php new file mode 100644 index 0000000000..d2e51d3c18 --- /dev/null +++ b/app/Services/Spectre/Object/Token.php @@ -0,0 +1,76 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Spectre\Object; + +use Carbon\Carbon; + +/** + * Class Token + */ +class Token extends SpectreObject +{ + /** @var string */ + private $connectUrl; + /** @var Carbon */ + private $expiresAt; + /** @var string */ + private $token; + + /** + * Token constructor. + * + * @param array $data + */ + public function __construct(array $data) + { + $this->token = $data['token']; + $this->expiresAt = new Carbon($data['expires_at']); + $this->connectUrl = $data['connect_url']; + } + + /** + * @return string + */ + public function getConnectUrl(): string + { + return $this->connectUrl; + } + + /** + * @return Carbon + */ + public function getExpiresAt(): Carbon + { + return $this->expiresAt; + } + + /** + * @return string + */ + public function getToken(): string + { + return $this->token; + } + +} \ No newline at end of file diff --git a/app/Services/Spectre/Request/CreateLoginRequest.php b/app/Services/Spectre/Request/CreateLoginRequest.php deleted file mode 100644 index 1b0e3e6b79..0000000000 --- a/app/Services/Spectre/Request/CreateLoginRequest.php +++ /dev/null @@ -1,109 +0,0 @@ -. - */ - -declare(strict_types=1); - -namespace FireflyIII\Services\Spectre\Request; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\SpectreProvider; -use FireflyIII\Services\Spectre\Object\Customer; - -/** - * Class CreateLoginRequest - */ -class CreateLoginRequest extends SpectreRequest -{ - /** @var Customer */ - private $customer; - /** @var array */ - private $mandatoryFields = []; - /** @var SpectreProvider */ - private $provider; - - /** - * - * @throws FireflyException - */ - public function call(): void - { - // add mandatory fields to login object - $data = [ - 'customer_id' => $this->customer->getId(), - 'country_code' => $this->provider->country_code, - 'provider_code' => $this->provider->code, - 'credentials' => $this->buildCredentials(), - 'daily_refresh' => true, - 'fetch_type' => 'recent', - 'include_fake_providers' => true, - ]; - $uri = '/api/v3/logins'; - $response = $this->sendSignedSpectrePost($uri, $data); - echo ''; - print_r($response); - exit; - } - - /** - * @param Customer $customer - */ - public function setCustomer(Customer $customer): void - { - $this->customer = $customer; - } - - /** - * @param array $mandatoryFields - */ - public function setMandatoryFields(array $mandatoryFields): void - { - $this->mandatoryFields = $mandatoryFields; - } - - /** - * @param SpectreProvider $provider - */ - public function setProvider(SpectreProvider $provider): void - { - $this->provider = $provider; - } - - /** - * @return array - * @throws FireflyException - */ - private function buildCredentials(): array - { - $return = []; - /** @var array $requiredField */ - foreach ($this->provider->data['required_fields'] as $requiredField) { - $fieldName = $requiredField['name']; - if (!isset($this->mandatoryFields[$fieldName])) { - throw new FireflyException(sprintf('Mandatory field "%s" is missing from job.', $fieldName)); - } - $return[$fieldName] = $this->mandatoryFields[$fieldName]; - } - - return $return; - } - - -} \ No newline at end of file diff --git a/app/Services/Spectre/Request/CreateTokenRequest.php b/app/Services/Spectre/Request/CreateTokenRequest.php new file mode 100644 index 0000000000..adacab4ae8 --- /dev/null +++ b/app/Services/Spectre/Request/CreateTokenRequest.php @@ -0,0 +1,94 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Spectre\Request; + +use FireflyIII\Services\Spectre\Object\Customer; +use FireflyIII\Services\Spectre\Object\Token; + + +/** + * Class CreateTokenRequest + */ +class CreateTokenRequest extends SpectreRequest +{ + /** @var Customer */ + private $customer; + + /** @var Token */ + private $token; + + /** @var string */ + private $uri; + + /** + * + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function call(): void + { + // add mandatory fields to login object + $data = [ + 'data' => [ + 'customer_id' => $this->customer->getId(), + 'fetch_type' => 'recent', + 'daily_refresh' => true, + 'include_fake_providers' => true, + 'show_consent_confirmation' => true, + 'credentials_strategy' => 'ask', + 'return_to' => $this->uri, + ], + ]; + $uri = '/api/v3/tokens/create'; + $response = $this->sendSignedSpectrePost($uri, $data); + $this->token = new Token($response['data']); + + return; + } + + /** + * @return Token + */ + public function getToken(): Token + { + return $this->token; + } + + /** + * @param Customer $customer + */ + public function setCustomer(Customer $customer): void + { + $this->customer = $customer; + } + + /** + * @param string $uri + */ + public function setUri(string $uri): void + { + $this->uri = $uri; + } + + +} \ No newline at end of file diff --git a/app/Services/Spectre/Request/ListLoginsRequest.php b/app/Services/Spectre/Request/ListLoginsRequest.php deleted file mode 100644 index d69af2a026..0000000000 --- a/app/Services/Spectre/Request/ListLoginsRequest.php +++ /dev/null @@ -1,101 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Services\Spectre\Request; - -use FireflyIII\Services\Spectre\Object\Customer; -use Log; - -/** - * Class ListLoginsRequest - */ -class ListLoginsRequest extends SpectreRequest -{ - /** @var Customer */ - protected $customer; - - /** @var array */ - protected $logins = []; - - /** - * - * @throws \FireflyIII\Exceptions\FireflyException - */ - public function call(): void - { - $hasNextPage = true; - $nextId = 0; - while ($hasNextPage) { - Log::debug(sprintf('Now calling list-logins for next_id %d', $nextId)); - $parameters = ['customer_id' => $this->customer->getId(), 'from_id' => $nextId]; - $uri = '/api/v3/logins?' . http_build_query($parameters); - $response = $this->sendSignedSpectreGet($uri, []); - - // count entries: - Log::debug(sprintf('Found %d entries in data-array', count($response['data']))); - - // extract next ID - $hasNextPage = false; - if (isset($response['meta']['next_id']) && intval($response['meta']['next_id']) > $nextId) { - $hasNextPage = true; - $nextId = $response['meta']['next_id']; - Log::debug(sprintf('Next ID is now %d.', $nextId)); - } else { - Log::debug('No next page.'); - } - - // store providers: - foreach ($response['data'] as $loginArray) { - var_dump($loginArray); - exit; - } - } - - return; - } - - /** - * @return Customer - */ - public function getCustomer(): Customer - { - return $this->customer; - } - - /** - * @param Customer $customer - */ - public function setCustomer(Customer $customer): void - { - $this->customer = $customer; - } - - /** - * @return array - */ - public function getLogins(): array - { - return $this->logins; - } - - -} diff --git a/app/Services/Spectre/Request/ListProvidersRequest.php b/app/Services/Spectre/Request/ListProvidersRequest.php deleted file mode 100644 index 966023904c..0000000000 --- a/app/Services/Spectre/Request/ListProvidersRequest.php +++ /dev/null @@ -1,81 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Services\Spectre\Request; - -use Log; - -/** - * Class ListProvidersRequest - */ -class ListProvidersRequest extends SpectreRequest -{ - /** - * @var array - */ - protected $providers = []; - - /** - * @throws \Exception - */ - public function call(): void - { - $hasNextPage = true; - $nextId = 0; - while ($hasNextPage) { - Log::debug(sprintf('Now calling for next_id %d', $nextId)); - $parameters = ['include_fake_providers' => 'true', 'include_provider_fields' => 'true', 'from_id' => $nextId]; - $uri = '/api/v3/providers?' . http_build_query($parameters); - $response = $this->sendSignedSpectreGet($uri, []); - - // count entries: - Log::debug(sprintf('Found %d entries in data-array', count($response['data']))); - - // extract next ID - $hasNextPage = false; - if (isset($response['meta']['next_id']) && intval($response['meta']['next_id']) > $nextId) { - $hasNextPage = true; - $nextId = $response['meta']['next_id']; - Log::debug(sprintf('Next ID is now %d.', $nextId)); - } else { - Log::debug('No next page.'); - } - - // store providers: - foreach ($response['data'] as $providerArray) { - $providerId = $providerArray['id']; - $this->providers[$providerId] = $providerArray; - Log::debug(sprintf('Stored provider #%d', $providerId)); - } - } - - return; - } - - /** - * @return array - */ - public function getProviders(): array - { - return $this->providers; - } -} diff --git a/app/Services/Spectre/Request/NewCustomerRequest.php b/app/Services/Spectre/Request/NewCustomerRequest.php index ff36919d70..0c6960be90 100644 --- a/app/Services/Spectre/Request/NewCustomerRequest.php +++ b/app/Services/Spectre/Request/NewCustomerRequest.php @@ -37,19 +37,13 @@ class NewCustomerRequest extends SpectreRequest */ public function call(): void { - $data = [ + $data = [ 'data' => [ 'identifier' => 'default_ff3_customer', ], ]; - $uri = '/api/v3/customers/'; - //$response = $this->sendSignedSpectrePost($uri, $data); - $response = ['data' => [ - 'id' => 527858, - 'identifier' => 'default_ff3_customer', - 'secret' => 'qpZjRPJRTb6mMcQgwDkssZ3fQVVDPIH04zBlkKC6MvI', - ], - ]; + $uri = '/api/v3/customers/'; + $response = $this->sendSignedSpectrePost($uri, $data); // create customer: $this->customer = new Customer($response['data']); diff --git a/app/Support/Import/Configuration/Spectre/InputMandatory.php b/app/Support/Import/Configuration/Spectre/InputMandatory.php deleted file mode 100644 index c6182ff53a..0000000000 --- a/app/Support/Import/Configuration/Spectre/InputMandatory.php +++ /dev/null @@ -1,121 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Support\Import\Configuration\Spectre; - -use Crypt; -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Models\ImportJob; -use FireflyIII\Models\SpectreProvider; -use FireflyIII\Support\Import\Configuration\ConfigurationInterface; - -/** - * Class InputMandatory - */ -class InputMandatory implements ConfigurationInterface -{ - /** @var ImportJob */ - private $job; - - /** - * Get the data necessary to show the configuration screen. - * - * @return array - * - * @throws FireflyException - */ - public function getData(): array - { - $config = $this->job->configuration; - $providerId = $config['provider']; - $provider = SpectreProvider::where('spectre_id', $providerId)->first(); - if (is_null($provider)) { - throw new FireflyException(sprintf('Cannot find Spectre provider with ID #%d', $providerId)); - } - $fields = $provider->data['required_fields'] ?? []; - $positions = []; - // Obtain a list of columns - foreach ($fields as $key => $row) { - $positions[$key] = $row['position']; - } - array_multisort($positions, SORT_ASC, $fields); - $country = SelectCountry::$allCountries[$config['country']] ?? $config['country']; - - return compact('provider', 'country', 'fields'); - } - - /** - * Return possible warning to user. - * - * @return string - */ - public function getWarningMessage(): string - { - return ''; - } - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job) - { - $this->job = $job; - } - - /** - * Store the result. - * - * @param array $data - * - * @return bool - * - * @throws FireflyException - */ - public function storeConfiguration(array $data): bool - { - $config = $this->job->configuration; - $providerId = $config['provider']; - $provider = SpectreProvider::where('spectre_id', $providerId)->first(); - if (is_null($provider)) { - throw new FireflyException(sprintf('Cannot find Spectre provider with ID #%d', $providerId)); - } - $mandatory = []; - $fields = $provider->data['required_fields'] ?? []; - foreach ($fields as $field) { - $name = $field['name']; - $mandatory[$name] = Crypt::encrypt($data[$name]) ?? null; - } - - // store in config of job: - $config['mandatory-fields'] = $mandatory; - $config['has-input-mandatory'] = true; - $this->job->configuration = $config; - $this->job->save(); - - // try to grab login for this job. See what happens? - // fire job that creates login object. user is redirected to "wait here" page (status page). Page should - // refresh and go back to interactive when user is supposed to enter SMS code or something. - // otherwise start downloading stuff - - return true; - } -} diff --git a/app/Support/Import/Configuration/Spectre/SelectCountry.php b/app/Support/Import/Configuration/Spectre/SelectCountry.php deleted file mode 100644 index eec76e8e54..0000000000 --- a/app/Support/Import/Configuration/Spectre/SelectCountry.php +++ /dev/null @@ -1,348 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Support\Import\Configuration\Spectre; - -use FireflyIII\Models\ImportJob; -use FireflyIII\Models\SpectreProvider; -use FireflyIII\Support\Import\Configuration\ConfigurationInterface; - -/** - * Class SelectCountry - */ -class SelectCountry implements ConfigurationInterface -{ - /** - * @var array - */ - public static $allCountries - = [ - 'AF' => 'Afghanistan', - 'AX' => 'Aland Islands', - 'AL' => 'Albania', - 'DZ' => 'Algeria', - 'AS' => 'American Samoa', - 'AD' => 'Andorra', - 'AO' => 'Angola', - 'AI' => 'Anguilla', - 'AQ' => 'Antarctica', - 'AG' => 'Antigua and Barbuda', - 'AR' => 'Argentina', - 'AM' => 'Armenia', - 'AW' => 'Aruba', - 'AU' => 'Australia', - 'AT' => 'Austria', - 'AZ' => 'Azerbaijan', - 'BS' => 'Bahamas', - 'BH' => 'Bahrain', - 'BD' => 'Bangladesh', - 'BB' => 'Barbados', - 'BY' => 'Belarus', - 'BE' => 'Belgium', - 'BZ' => 'Belize', - 'BJ' => 'Benin', - 'BM' => 'Bermuda', - 'BT' => 'Bhutan', - 'BO' => 'Bolivia', - 'BQ' => 'Bonaire, Saint Eustatius and Saba', - 'BA' => 'Bosnia and Herzegovina', - 'BW' => 'Botswana', - 'BV' => 'Bouvet Island', - 'BR' => 'Brazil', - 'IO' => 'British Indian Ocean Territory', - 'VG' => 'British Virgin Islands', - 'BN' => 'Brunei', - 'BG' => 'Bulgaria', - 'BF' => 'Burkina Faso', - 'BI' => 'Burundi', - 'KH' => 'Cambodia', - 'CM' => 'Cameroon', - 'CA' => 'Canada', - 'CV' => 'Cape Verde', - 'KY' => 'Cayman Islands', - 'CF' => 'Central African Republic', - 'TD' => 'Chad', - 'CL' => 'Chile', - 'CN' => 'China', - 'CX' => 'Christmas Island', - 'CC' => 'Cocos Islands', - 'CO' => 'Colombia', - 'KM' => 'Comoros', - 'CK' => 'Cook Islands', - 'CR' => 'Costa Rica', - 'HR' => 'Croatia', - 'CU' => 'Cuba', - 'CW' => 'Curacao', - 'CY' => 'Cyprus', - 'CZ' => 'Czech Republic', - 'CD' => 'Democratic Republic of the Congo', - 'DK' => 'Denmark', - 'DJ' => 'Djibouti', - 'DM' => 'Dominica', - 'DO' => 'Dominican Republic', - 'TL' => 'East Timor', - 'EC' => 'Ecuador', - 'EG' => 'Egypt', - 'SV' => 'El Salvador', - 'GQ' => 'Equatorial Guinea', - 'ER' => 'Eritrea', - 'EE' => 'Estonia', - 'ET' => 'Ethiopia', - 'FK' => 'Falkland Islands', - 'FO' => 'Faroe Islands', - 'FJ' => 'Fiji', - 'FI' => 'Finland', - 'FR' => 'France', - 'GF' => 'French Guiana', - 'PF' => 'French Polynesia', - 'TF' => 'French Southern Territories', - 'GA' => 'Gabon', - 'GM' => 'Gambia', - 'GE' => 'Georgia', - 'DE' => 'Germany', - 'GH' => 'Ghana', - 'GI' => 'Gibraltar', - 'GR' => 'Greece', - 'GL' => 'Greenland', - 'GD' => 'Grenada', - 'GP' => 'Guadeloupe', - 'GU' => 'Guam', - 'GT' => 'Guatemala', - 'GG' => 'Guernsey', - 'GN' => 'Guinea', - 'GW' => 'Guinea-Bissau', - 'GY' => 'Guyana', - 'HT' => 'Haiti', - 'HM' => 'Heard Island and McDonald Islands', - 'HN' => 'Honduras', - 'HK' => 'Hong Kong', - 'HU' => 'Hungary', - 'IS' => 'Iceland', - 'IN' => 'India', - 'ID' => 'Indonesia', - 'IR' => 'Iran', - 'IQ' => 'Iraq', - 'IE' => 'Ireland', - 'IM' => 'Isle of Man', - 'IL' => 'Israel', - 'IT' => 'Italy', - 'CI' => 'Ivory Coast', - 'JM' => 'Jamaica', - 'JP' => 'Japan', - 'JE' => 'Jersey', - 'JO' => 'Jordan', - 'KZ' => 'Kazakhstan', - 'KE' => 'Kenya', - 'KI' => 'Kiribati', - 'XK' => 'Kosovo', - 'KW' => 'Kuwait', - 'KG' => 'Kyrgyzstan', - 'LA' => 'Laos', - 'LV' => 'Latvia', - 'LB' => 'Lebanon', - 'LS' => 'Lesotho', - 'LR' => 'Liberia', - 'LY' => 'Libya', - 'LI' => 'Liechtenstein', - 'LT' => 'Lithuania', - 'LU' => 'Luxembourg', - 'MO' => 'Macao', - 'MK' => 'Macedonia', - 'MG' => 'Madagascar', - 'MW' => 'Malawi', - 'MY' => 'Malaysia', - 'MV' => 'Maldives', - 'ML' => 'Mali', - 'MT' => 'Malta', - 'MH' => 'Marshall Islands', - 'MQ' => 'Martinique', - 'MR' => 'Mauritania', - 'MU' => 'Mauritius', - 'YT' => 'Mayotte', - 'MX' => 'Mexico', - 'FM' => 'Micronesia', - 'MD' => 'Moldova', - 'MC' => 'Monaco', - 'MN' => 'Mongolia', - 'ME' => 'Montenegro', - 'MS' => 'Montserrat', - 'MA' => 'Morocco', - 'MZ' => 'Mozambique', - 'MM' => 'Myanmar', - 'NA' => 'Namibia', - 'NR' => 'Nauru', - 'NP' => 'Nepal', - 'NL' => 'Netherlands', - 'NC' => 'New Caledonia', - 'NZ' => 'New Zealand', - 'NI' => 'Nicaragua', - 'NE' => 'Niger', - 'NG' => 'Nigeria', - 'NU' => 'Niue', - 'NF' => 'Norfolk Island', - 'KP' => 'North Korea', - 'MP' => 'Northern Mariana Islands', - 'NO' => 'Norway', - 'OM' => 'Oman', - 'PK' => 'Pakistan', - 'PW' => 'Palau', - 'PS' => 'Palestinian Territory', - 'PA' => 'Panama', - 'PG' => 'Papua New Guinea', - 'PY' => 'Paraguay', - 'PE' => 'Peru', - 'PH' => 'Philippines', - 'PN' => 'Pitcairn', - 'PL' => 'Poland', - 'PT' => 'Portugal', - 'PR' => 'Puerto Rico', - 'QA' => 'Qatar', - 'CG' => 'Republic of the Congo', - 'RE' => 'Reunion', - 'RO' => 'Romania', - 'RU' => 'Russia', - 'RW' => 'Rwanda', - 'BL' => 'Saint Barthelemy', - 'SH' => 'Saint Helena', - 'KN' => 'Saint Kitts and Nevis', - 'LC' => 'Saint Lucia', - 'MF' => 'Saint Martin', - 'PM' => 'Saint Pierre and Miquelon', - 'VC' => 'Saint Vincent and the Grenadines', - 'WS' => 'Samoa', - 'SM' => 'San Marino', - 'ST' => 'Sao Tome and Principe', - 'SA' => 'Saudi Arabia', - 'SN' => 'Senegal', - 'RS' => 'Serbia', - 'SC' => 'Seychelles', - 'SL' => 'Sierra Leone', - 'SG' => 'Singapore', - 'SX' => 'Sint Maarten', - 'SK' => 'Slovakia', - 'SI' => 'Slovenia', - 'SB' => 'Solomon Islands', - 'SO' => 'Somalia', - 'ZA' => 'South Africa', - 'GS' => 'South Georgia and the South Sandwich Islands', - 'KR' => 'South Korea', - 'SS' => 'South Sudan', - 'ES' => 'Spain', - 'LK' => 'Sri Lanka', - 'SD' => 'Sudan', - 'SR' => 'Suriname', - 'SJ' => 'Svalbard and Jan Mayen', - 'SZ' => 'Swaziland', - 'SE' => 'Sweden', - 'CH' => 'Switzerland', - 'SY' => 'Syria', - 'TW' => 'Taiwan', - 'TJ' => 'Tajikistan', - 'TZ' => 'Tanzania', - 'TH' => 'Thailand', - 'TG' => 'Togo', - 'TK' => 'Tokelau', - 'TO' => 'Tonga', - 'TT' => 'Trinidad and Tobago', - 'TN' => 'Tunisia', - 'TR' => 'Turkey', - 'TM' => 'Turkmenistan', - 'TC' => 'Turks and Caicos Islands', - 'TV' => 'Tuvalu', - 'VI' => 'U.S. Virgin Islands', - 'UG' => 'Uganda', - 'UA' => 'Ukraine', - 'AE' => 'United Arab Emirates', - 'GB' => 'United Kingdom', - 'US' => 'United States', - 'UM' => 'United States Minor Outlying Islands', - 'UY' => 'Uruguay', - 'UZ' => 'Uzbekistan', - 'VU' => 'Vanuatu', - 'VA' => 'Vatican', - 'VE' => 'Venezuela', - 'VN' => 'Vietnam', - 'WF' => 'Wallis and Futuna', - 'EH' => 'Western Sahara', - 'YE' => 'Yemen', - 'ZM' => 'Zambia', - 'ZW' => 'Zimbabwe', - 'XF' => 'Fake Country (for testing)', - 'XO' => 'Other financial applications', - ]; - /** @var ImportJob */ - private $job; - - /** - * Get the data necessary to show the configuration screen. - * - * @return array - */ - public function getData(): array - { - $providers = SpectreProvider::get(); - $countries = []; - /** @var SpectreProvider $provider */ - foreach ($providers as $provider) { - $countries[$provider->country_code] = self::$allCountries[$provider->country_code] ?? $provider->country_code; - } - asort($countries); - - return compact('countries'); - } - - /** - * Return possible warning to user. - * - * @return string - */ - public function getWarningMessage(): string - { - return ''; - } - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job) - { - $this->job = $job; - } - - /** - * Store the result. - * - * @param array $data - * - * @return bool - */ - public function storeConfiguration(array $data): bool - { - $config = $this->job->configuration; - $config['country'] = $data['country_code'] ?? 'XF'; // default to fake country. - $config['selected-country'] = true; - $this->job->configuration = $config; - $this->job->save(); - - return true; - } -} diff --git a/app/Support/Import/Configuration/Spectre/SelectProvider.php b/app/Support/Import/Configuration/Spectre/SelectProvider.php deleted file mode 100644 index 843a722c04..0000000000 --- a/app/Support/Import/Configuration/Spectre/SelectProvider.php +++ /dev/null @@ -1,93 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Support\Import\Configuration\Spectre; - -use FireflyIII\Models\ImportJob; -use FireflyIII\Models\SpectreProvider; -use FireflyIII\Support\Import\Configuration\ConfigurationInterface; - -/** - * Class SelectProvider - */ -class SelectProvider implements ConfigurationInterface -{ - /** @var ImportJob */ - private $job; - - /** - * Get the data necessary to show the configuration screen. - * - * @return array - */ - public function getData(): array - { - $config = $this->job->configuration; - $selection = SpectreProvider::where('country_code', $config['country'])->where('status', 'active')->get(); - $providers = []; - /** @var SpectreProvider $provider */ - foreach ($selection as $provider) { - $providerId = $provider->spectre_id; - $name = $provider->data['name']; - $providers[$providerId] = $name; - } - $country = SelectCountry::$allCountries[$config['country']] ?? $config['country']; - - return compact('providers', 'country'); - } - - /** - * Return possible warning to user. - * - * @return string - */ - public function getWarningMessage(): string - { - return ''; - } - - /** - * @param ImportJob $job - */ - public function setJob(ImportJob $job) - { - $this->job = $job; - } - - /** - * Store the result. - * - * @param array $data - * - * @return bool - */ - public function storeConfiguration(array $data): bool - { - $config = $this->job->configuration; - $config['provider'] = intval($data['provider_code']) ?? 0; // default to fake country. - $config['selected-provider'] = true; - $this->job->configuration = $config; - $this->job->save(); - - return true; - } -} diff --git a/app/Support/Import/Information/SpectreInformation.php b/app/Support/Import/Information/SpectreInformation.php deleted file mode 100644 index 0cc93f8992..0000000000 --- a/app/Support/Import/Information/SpectreInformation.php +++ /dev/null @@ -1,217 +0,0 @@ -. - */ -declare(strict_types=1); - -namespace FireflyIII\Support\Import\Information; - -use FireflyIII\Exceptions\FireflyException; -use FireflyIII\Services\Bunq\Object\Alias; -use FireflyIII\Services\Bunq\Object\MonetaryAccountBank; -use FireflyIII\Services\Bunq\Request\DeleteDeviceSessionRequest; -use FireflyIII\Services\Bunq\Request\DeviceSessionRequest; -use FireflyIII\Services\Bunq\Request\ListMonetaryAccountRequest; -use FireflyIII\Services\Bunq\Request\ListUserRequest; -use FireflyIII\Services\Bunq\Token\SessionToken; -use FireflyIII\Support\CacheProperties; -use FireflyIII\User; -use Illuminate\Support\Collection; -use Log; -use Preferences; - -/** - * Class SpectreInformation - */ -class SpectreInformation implements InformationInterface -{ - /** @var User */ - private $user; - - /** - * Returns a collection of accounts. Preferrably, these follow a uniform Firefly III format so they can be managed over banks. - * - * The format for these bank accounts is basically this: - * - * id: bank specific id - * name: bank appointed name - * number: account number (usually IBAN) - * currency: ISO code of currency - * balance: current balance - * - * - * any other fields are optional but can be useful: - * image: logo or account specific thing - * color: any associated color. - * - * @return array - * - * @throws FireflyException - * @throws \Exception - */ - public function getAccounts(): array - { - // cache for an hour: - $cache = new CacheProperties; - $cache->addProperty('bunq.get-accounts'); - $cache->addProperty(date('dmy h')); - if ($cache->has()) { - return $cache->get(); // @codeCoverageIgnore - } - Log::debug('Now in getAccounts()'); - $sessionToken = $this->startSession(); - $userId = $this->getUserInformation($sessionToken); - // get list of Bunq accounts: - $accounts = $this->getMonetaryAccounts($sessionToken, $userId); - $return = []; - /** @var MonetaryAccountBank $account */ - foreach ($accounts as $account) { - $current = [ - 'id' => $account->getId(), - 'name' => $account->getDescription(), - 'currency' => $account->getCurrency(), - 'balance' => $account->getBalance()->getValue(), - 'color' => $account->getSetting()->getColor(), - ]; - /** @var Alias $alias */ - foreach ($account->getAliases() as $alias) { - if ('IBAN' === $alias->getType()) { - $current['number'] = $alias->getValue(); - } - } - $return[] = $current; - } - $cache->store($return); - - $this->closeSession($sessionToken); - - return $return; - } - - /** - * Set the user for this Prerequisites-routine. Class is expected to implement and save this. - * - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - } - - /** - * @param SessionToken $sessionToken - * - * @throws \Exception - */ - private function closeSession(SessionToken $sessionToken): void - { - Log::debug('Going to close session'); - $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; - $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; - $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; - $request = new DeleteDeviceSessionRequest(); - $request->setSecret($apiKey); - $request->setPrivateKey($privateKey); - $request->setServerPublicKey($serverPublicKey); - $request->setSessionToken($sessionToken); - $request->call(); - - return; - } - - /** - * @param SessionToken $sessionToken - * @param int $userId - * - * @return Collection - * - * @throws \Exception - */ - private function getMonetaryAccounts(SessionToken $sessionToken, int $userId): Collection - { - $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; - $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; - $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; - $request = new ListMonetaryAccountRequest; - - $request->setSessionToken($sessionToken); - $request->setSecret($apiKey); - $request->setServerPublicKey($serverPublicKey); - $request->setPrivateKey($privateKey); - $request->setUserId($userId); - $request->call(); - - return $request->getMonetaryAccounts(); - } - - /** - * @param SessionToken $sessionToken - * - * @return int - * - * @throws FireflyException - * @throws \Exception - */ - private function getUserInformation(SessionToken $sessionToken): int - { - $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; - $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; - $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; - $request = new ListUserRequest; - $request->setSessionToken($sessionToken); - $request->setSecret($apiKey); - $request->setServerPublicKey($serverPublicKey); - $request->setPrivateKey($privateKey); - $request->call(); - // return the first that isn't null? - $company = $request->getUserCompany(); - if ($company->getId() > 0) { - return $company->getId(); - } - $user = $request->getUserPerson(); - if ($user->getId() > 0) { - return $user->getId(); - } - throw new FireflyException('Expected user or company from Bunq, but got neither.'); - } - - /** - * @return SessionToken - * - * @throws \Exception - */ - private function startSession(): SessionToken - { - Log::debug('Now in startSession.'); - $apiKey = Preferences::getForUser($this->user, 'bunq_api_key')->data; - $serverPublicKey = Preferences::getForUser($this->user, 'bunq_server_public_key')->data; - $privateKey = Preferences::getForUser($this->user, 'bunq_private_key')->data; - $installationToken = Preferences::getForUser($this->user, 'bunq_installation_token')->data; - $request = new DeviceSessionRequest(); - $request->setSecret($apiKey); - $request->setServerPublicKey($serverPublicKey); - $request->setPrivateKey($privateKey); - $request->setInstallationToken($installationToken); - $request->call(); - $sessionToken = $request->getSessionToken(); - Log::debug(sprintf('Now have got session token: %s', serialize($sessionToken))); - - return $sessionToken; - } -} diff --git a/public/js/ff/import/status.js b/public/js/ff/import/status.js index 0f65e71b60..9fa0198516 100644 --- a/public/js/ff/import/status.js +++ b/public/js/ff/import/status.js @@ -34,10 +34,12 @@ var knownErrors = 0; $(function () { "use strict"; + console.log('in start'); timeOutId = setTimeout(checkJobStatus, startInterval); $('.start-job').click(startJob); if (job.configuration['auto-start']) { + console.log('Called startJob()!'); startJob(); } }); @@ -46,6 +48,7 @@ $(function () { * Downloads some JSON and responds to its content to see what the status is of the current import. */ function checkJobStatus() { + console.log('in checkJobStatus'); $.getJSON(jobStatusUri).done(reportOnJobStatus).fail(reportFailedJob); } @@ -53,6 +56,7 @@ function checkJobStatus() { * This method is called when the JSON query returns an error. If possible, this error is relayed to the user. */ function reportFailedJob(jqxhr, textStatus, error) { + console.log('in reportFailedJob'); // hide all possible boxes: $('.statusbox').hide(); @@ -72,6 +76,7 @@ function reportFailedJob(jqxhr, textStatus, error) { * @param data */ function reportOnJobStatus(data) { + console.log('in reportOnJobStatus: ' + data.status); switch (data.status) { case "configured": @@ -80,6 +85,10 @@ function reportOnJobStatus(data) { $('.statusbox').hide(); $('.status_configured').show(); } + if (job.configuration['auto-start']) { + console.log('Job is auto start. Check status again in 500ms.'); + timeOutId = setTimeout(checkJobStatus, interval); + } break; case "running": // job is running! Show the running box: @@ -122,7 +131,13 @@ function reportOnJobStatus(data) { // show the fatal error box: $('.fatal_error').show(); break; + case "configuring": + // redirect back to configure screen. + console.log('Will now redirect to ' + jobConfigureUri); + window.location = jobConfigureUri; + break; default: + console.error('Cannot handle job status ' + data.status); break; } diff --git a/resources/views/import/spectre/redirect.twig b/resources/views/import/spectre/redirect.twig new file mode 100644 index 0000000000..e76c8b4bdb --- /dev/null +++ b/resources/views/import/spectre/redirect.twig @@ -0,0 +1,15 @@ + + + + + + +Page Redirection + + + +If you are not redirected automatically, follow this link to Spectre.. + +#} \ No newline at end of file diff --git a/resources/views/import/status.twig b/resources/views/import/status.twig index 2844f818fa..650c224fac 100644 --- a/resources/views/import/status.twig +++ b/resources/views/import/status.twig @@ -149,6 +149,7 @@ var langImportMultiError = '{{ trans('import.status_errors_multi')|escape('js') }}'; var jobStatusUri = '{{ route('import.status.json', [job.key]) }}'; var jobStartUri = '{{ route('import.start', [job.key]) }}'; + var jobConfigureUri = '{{ route('import.configure', [job.key]) }}'; var token = '{{ csrf_token() }}'; var job = {{ job|json_encode|raw }};