GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( 59d732...3562ec )
by James
28:30 queued 12:37
created

SpectreRoutine::runStageHaveMapping()   B

Complexity

Conditions 4
Paths 5

Size

Total Lines 27
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 27
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 16
nc 5
nop 0
1
<?php
2
/**
3
 * SpectreRoutine.php
4
 * Copyright (c) 2017 [email protected]
5
 *
6
 * This file is part of Firefly III.
7
 *
8
 * Firefly III is free software: you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * Firefly III is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with Firefly III. If not, see <http://www.gnu.org/licenses/>.
20
 */
21
declare(strict_types=1);
22
23
namespace FireflyIII\Import\Routine;
24
25
use FireflyIII\Exceptions\FireflyException;
26
use FireflyIII\Models\ImportJob;
27
use FireflyIII\Repositories\ImportJob\ImportJobRepositoryInterface;
28
use FireflyIII\Services\Spectre\Exception\DuplicatedCustomerException;
29
use FireflyIII\Services\Spectre\Exception\SpectreException;
30
use FireflyIII\Services\Spectre\Object\Account;
31
use FireflyIII\Services\Spectre\Object\Customer;
32
use FireflyIII\Services\Spectre\Object\Login;
33
use FireflyIII\Services\Spectre\Object\Token;
34
use FireflyIII\Services\Spectre\Request\CreateTokenRequest;
35
use FireflyIII\Services\Spectre\Request\ListAccountsRequest;
36
use FireflyIII\Services\Spectre\Request\ListCustomersRequest;
37
use FireflyIII\Services\Spectre\Request\ListLoginsRequest;
38
use FireflyIII\Services\Spectre\Request\ListTransactionsRequest;
39
use FireflyIII\Services\Spectre\Request\NewCustomerRequest;
40
use Illuminate\Support\Collection;
41
use Log;
42
use Preferences;
43
44
/**
45
 * Class FileRoutine
46
 */
47
class SpectreRoutine implements RoutineInterface
48
{
49
    /** @var Collection */
50
    public $errors;
51
    /** @var Collection */
52
    public $journals;
53
    /** @var int */
54
    public $lines = 0;
55
    /** @var ImportJob */
56
    private $job;
57
58
    /** @var ImportJobRepositoryInterface */
59
    private $repository;
60
61
    /**
62
     * ImportRoutine constructor.
63
     */
64
    public function __construct()
65
    {
66
        $this->journals = new Collection;
67
        $this->errors   = new Collection;
68
    }
69
70
    /**
71
     * @return Collection
72
     */
73
    public function getErrors(): Collection
74
    {
75
        return $this->errors;
76
    }
77
78
    /**
79
     * @return Collection
80
     */
81
    public function getJournals(): Collection
82
    {
83
        return $this->journals;
84
    }
85
86
    /**
87
     * @return int
88
     */
89
    public function getLines(): int
90
    {
91
        return $this->lines;
92
    }
93
94
    /**
95
     * A Spectre job that ends up here is either "configured" or "running", and will be set to "running"
96
     * when it is "configured".
97
     *
98
     * Job has several stages, stored in extended status key 'stage'
99
     *
100
     * initial: just begun, nothing happened. action: get a customer and a token. Next status: has-token
101
     * has-token: redirect user to sandstorm, make user login. set job to: user-logged-in
102
     * user-logged-in: customer has an attempt. action: analyse/get attempt and go for next status.
103
     *                 if attempt failed: job status is error, save a warning somewhere?
104
     *                 if success, try to get accounts. Save in config key 'accounts'. set status: have-accounts and "configuring"
105
     *
106
     * have-accounts: make user link accounts and select accounts to import from.
107
     *
108
     * If job is "configuring" and stage "have-accounts" then present the accounts and make user link them to
109
     * own asset accounts. Store this mapping, set config to "have-account-mapping" and job status configured".
110
     *
111
     * have-account-mapping: start downloading transactions?
112
     *
113
     *
114
     * @throws \FireflyIII\Exceptions\FireflyException
115
     * @throws \FireflyIII\Services\Spectre\Exception\SpectreException
116
     */
117
    public function run(): bool
118
    {
119
        if ('configured' === $this->job->status) {
120
            $this->repository->updateStatus($this->job, 'running');
121
        }
122
        Log::info(sprintf('Start with import job %s using Spectre.', $this->job->key));
123
        set_time_limit(0);
124
125
        // check if job has token first!
126
        $config = $this->job->configuration;
127
        $stage  = $config['stage'];
128
129
        switch ($stage) {
130
            case 'initial':
131
                // get customer and token:
132
                $this->runStageInitial();
133
                break;
134
            case 'has-token':
135
                // import routine does nothing at this point:
136
                break;
137
            case 'user-logged-in':
138
                $this->runStageLoggedIn();
139
                break;
140
            case 'have-account-mapping':
141
                $this->runStageHaveMapping();
142
                break;
143
            default:
144
                throw new FireflyException(sprintf('Cannot handle stage %s', $stage));
145
        }
146
147
        var_dump($config);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($config); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
148
        exit;
149
150
        throw new FireflyException('Application cannot handle this.');
0 ignored issues
show
Unused Code introduced by
throw new \FireflyIII\Ex... cannot handle this.'); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
151
    }
152
153
    /**
154
     * @param ImportJob $job
155
     */
156
    public function setJob(ImportJob $job)
157
    {
158
        $this->job        = $job;
159
        $this->repository = app(ImportJobRepositoryInterface::class);
160
        $this->repository->setUser($job->user);
161
    }
162
163
    /**
164
     * @return Customer
165
     * @throws \FireflyIII\Exceptions\FireflyException
166
     * @throws \FireflyIII\Services\Spectre\Exception\SpectreException
167
     */
168
    protected function createCustomer(): Customer
169
    {
170
        $newCustomerRequest = new NewCustomerRequest($this->job->user);
171
        $customer           = null;
172
        try {
173
            $newCustomerRequest->call();
174
            $customer = $newCustomerRequest->getCustomer();
175
        } catch (DuplicatedCustomerException $e) {
176
            // already exists, must fetch customer instead.
177
            Log::warning('Customer exists already for user, fetch it.');
178
        }
179
        if (is_null($customer)) {
180
            $getCustomerRequest = new ListCustomersRequest($this->job->user);
181
            $getCustomerRequest->call();
182
            $customers = $getCustomerRequest->getCustomers();
183
            /** @var Customer $current */
184
            foreach ($customers as $current) {
185
                if ($current->getIdentifier() === 'default_ff3_customer') {
186
                    $customer = $current;
187
                    break;
188
                }
189
            }
190
        }
191
192
        Preferences::setForUser($this->job->user, 'spectre_customer', $customer->toArray());
193
194
        return $customer;
195
196
    }
197
198
    /**
199
     * @return Customer
200
     * @throws FireflyException
201
     * @throws \FireflyIII\Services\Spectre\Exception\SpectreException
202
     */
203
    protected function getCustomer(): Customer
204
    {
205
        $config = $this->job->configuration;
206
        if (!is_null($config['customer'])) {
207
            $customer = new Customer($config['customer']);
208
209
            return $customer;
210
        }
211
212
        $customer                 = $this->createCustomer();
213
        $config['customer']       = [
214
            'id'         => $customer->getId(),
215
            'identifier' => $customer->getIdentifier(),
216
            'secret'     => $customer->getSecret(),
217
        ];
218
        $this->job->configuration = $config;
219
        $this->job->save();
220
221
        return $customer;
222
    }
223
224
    /**
225
     * @param Customer $customer
226
     * @param string   $returnUri
227
     *
228
     * @return Token
229
     * @throws \FireflyIII\Exceptions\FireflyException
230
     * @throws \FireflyIII\Services\Spectre\Exception\SpectreException
231
     */
232
    protected function getToken(Customer $customer, string $returnUri): Token
233
    {
234
        $request = new CreateTokenRequest($this->job->user);
235
        $request->setUri($returnUri);
236
        $request->setCustomer($customer);
237
        $request->call();
238
        Log::debug('Call to get token is finished');
239
240
        return $request->getToken();
241
242
    }
243
244
    /**
245
     * @throws FireflyException
246
     * @throws SpectreException
247
     */
248
    protected function runStageInitial(): void
249
    {
250
        Log::debug('In runStageInitial()');
251
252
        // create customer if user does not have one:
253
        $customer = $this->getCustomer();
254
        Log::debug(sprintf('Customer ID is %s', $customer->getId()));
255
256
        // use customer to request a token:
257
        $uri   = route('import.status', [$this->job->key]);
258
        $token = $this->getToken($customer, $uri);
259
        Log::debug(sprintf('Token is %s', $token->getToken()));
260
261
        // update job, give it the token:
262
        $config                   = $this->job->configuration;
263
        $config['has-token']      = true;
264
        $config['token']          = $token->getToken();
265
        $config['token-expires']  = $token->getExpiresAt()->format('U');
266
        $config['token-url']      = $token->getConnectUrl();
267
        $config['stage']          = 'has-token';
268
        $this->job->configuration = $config;
269
270
        Log::debug('Job config is now', $config);
271
272
        // update job, set status to "configuring".
273
        $this->job->status = 'configuring';
274
        $this->job->save();
275
        Log::debug(sprintf('Job status is now %s', $this->job->status));
276
    }
277
278
    /**
279
     * @throws FireflyException
280
     * @throws SpectreException
281
     */
282
    protected function runStageLoggedIn(): void
283
    {
284
        Log::debug('In runStageLoggedIn');
285
        // list all logins:
286
        $customer = $this->getCustomer();
287
        $request  = new ListLoginsRequest($this->job->user);
288
        $request->setCustomer($customer);
289
        $request->call();
290
        $logins = $request->getLogins();
291
        /** @var Login $final */
292
        $final = null;
293
        // loop logins, find the latest with no error in it:
294
        $time = 0;
295
        /** @var Login $login */
296
        foreach ($logins as $login) {
297
            $attempt     = $login->getLastAttempt();
298
            $attemptTime = intval($attempt->getCreatedAt()->format('U'));
299
            if ($attemptTime > $time && is_null($attempt->getFailErrorClass())) {
300
                $time  = $attemptTime;
301
                $final = $login;
302
            }
303
        }
304
        if (is_null($final)) {
305
            throw new FireflyException('No valid login attempt found.');
306
        }
307
308
        // list the users accounts using this login.
309
        $accountRequest = new ListAccountsRequest($this->job->user);
310
        $accountRequest->setLogin($login);
0 ignored issues
show
Bug introduced by
The variable $login seems to be defined by a foreach iteration on line 296. Are you sure the iterator is never empty, otherwise this variable is not defined?

It seems like you are relying on a variable being defined by an iteration:

foreach ($a as $b) {
}

// $b is defined here only if $a has elements, for example if $a is array()
// then $b would not be defined here. To avoid that, we recommend to set a
// default value for $b.


// Better
$b = 0; // or whatever default makes sense in your context
foreach ($a as $b) {
}

// $b is now guaranteed to be defined here.
Loading history...
311
        $accountRequest->call();
312
        $accounts = $accountRequest->getAccounts();
313
314
        // store accounts in job:
315
        $all = [];
316
        /** @var Account $account */
317
        foreach ($accounts as $account) {
318
            $all[] = $account->toArray();
319
        }
320
321
        // update job:
322
        $config                   = $this->job->configuration;
323
        $config['accounts']       = $all;
324
        $config['login']          = $login->toArray();
325
        $config['stage']          = 'have-accounts';
326
        $this->job->configuration = $config;
327
        $this->job->status        = 'configuring';
328
        $this->job->save();
329
330
        return;
331
    }
332
333
    /**
334
     *
335
     */
336
    private function runStageHaveMapping()
337
    {
338
        // for each spectre account id in 'account-mappings'.
339
        // find FF account
340
        // get transactions.
341
        // import?!
342
        $config   = $this->job->configuration;
343
        $accounts = $config['accounts'] ?? [];
344
        /** @var array $accountArray */
345
        foreach ($accounts as $accountArray) {
346
            $account  = new Account($accountArray);
347
            $importId = intval($config['accounts-mapped'][$account->getid()] ?? 0);
348
            $doImport = $importId !== 0 ? true : false;
349
            if (!$doImport) {
350
                continue;
351
            }
352
            // import into account
353
            $listTransactionsRequest = new ListTransactionsRequest($this->job->user);
354
            $listTransactionsRequest->setAccount($account);
355
            $listTransactionsRequest->call();
356
            $transactions = $listTransactionsRequest->getTransactions();
357
            var_dump($transactions);exit;
0 ignored issues
show
Security Debugging Code introduced by
var_dump($transactions); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
Coding Style introduced by
It is generally recommended to place each PHP statement on a line by itself.

Let’s take a look at an example:

// Bad
$a = 5; $b = 6; $c = 7;

// Good
$a = 5;
$b = 6;
$c = 7;
Loading history...
358
359
        }
360
        var_dump($config);
361
        exit;
362
    }
363
}
364