Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
The commit updates the return type of filter methods in `Contacts.php` and `Invoices.php` from `static` to explicit class names, for better clarity and stability. It also changes some uses of fully qualified class names in `XeroAuthenticated.php` and `XeroShowAllCommand.php` to uses of `use` statements for better readability. Unnecessary namespace in `XeroShowAllCommand.php` was removed.
  • Loading branch information
dcblogdev committed Aug 31, 2024
1 parent 4d62450 commit 58299d4
Show file tree
Hide file tree
Showing 7 changed files with 153 additions and 2 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"autoload": {
"psr-4": {
"Dcblogdev\\Xero\\": "src/",
"Dcblogdev\\Xero\\database\\factories\\": "database/factories/",
"Dcblogdev\\Xero\\Tests\\": "tests"
}
},
Expand Down
29 changes: 29 additions & 0 deletions src/Actions/tokenExpiredAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Dcblogdev\Xero\Actions;

use Dcblogdev\Xero\Exceptions\XeroTokenExpiredException;
use Dcblogdev\Xero\Models\XeroToken;
use Exception;
use Illuminate\Http\RedirectResponse;

class tokenExpiredAction
{
/**
* @throws Exception
*/
public function handle(array $result, XeroToken $token): ?RedirectResponse
{
if (isset($result['error']) && $result['error'] === 'invalid_grant') {
$token->delete();

if (app()->runningInConsole()) {
throw new Exception('Xero token has expired, please re-authenticate.');
} else {
throw new XeroTokenExpiredException('Xero token has expired, please re-authenticate.');
}
}

return null;
}
}
14 changes: 14 additions & 0 deletions src/Exceptions/XeroTokenExpiredException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Dcblogdev\Xero\Exceptions;

use Exception;
use Illuminate\Http\RedirectResponse;

class XeroTokenExpiredException extends Exception
{
public function render(): RedirectResponse
{
return redirect()->away(config('xero.redirectUri'))->with('error', $this->getMessage());
}
}
9 changes: 9 additions & 0 deletions src/Models/XeroToken.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Dcblogdev\Xero\Models;

use DateTimeInterface;
use Dcblogdev\Xero\database\factories\TokenFactory;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Carbon;

Expand All @@ -20,8 +22,15 @@
*/
class XeroToken extends Model
{
use HasFactory;

protected $guarded = [];

protected static function newFactory(): TokenFactory
{
return TokenFactory::new();
}

/**
* @return Attribute<Carbon, never>
*/
Expand Down
6 changes: 4 additions & 2 deletions src/Xero.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Dcblogdev\Xero;

use Dcblogdev\Xero\Actions\tokenExpiredAction;
use Dcblogdev\Xero\Models\XeroToken;
use Dcblogdev\Xero\Resources\Contacts;
use Dcblogdev\Xero\Resources\CreditNotes;
Expand Down Expand Up @@ -192,7 +193,7 @@ public function getAccessToken(bool $redirectWhenNotConnected = true): string
/**
* @throws Exception
*/
public function renewExpiringToken($token)
public function renewExpiringToken(XeroToken $token, tokenExpiredAction $tokenExpiredAction): string
{
$params = [
'grant_type' => 'refresh_token',
Expand All @@ -202,6 +203,8 @@ public function renewExpiringToken($token)

$result = $this->sendPost(self::$tokenUrl, $params);

$tokenExpiredAction->handle($result, $token);

$this->storeToken($result, ['tenant_id' => $token->tenant_id]);

return $result['access_token'];
Expand Down Expand Up @@ -332,7 +335,6 @@ protected function guzzle(string $type, string $request, array $data = [], bool
protected static function sendPost(string $url, array $params)
{
try {

$response = Http::withHeaders([
'authorization' => "Basic " . base64_encode(config('xero.clientId') . ":" . config('xero.clientSecret'))
])
Expand Down
25 changes: 25 additions & 0 deletions src/database/factories/TokenFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

namespace Dcblogdev\Xero\database\factories;

use Dcblogdev\Xero\Models\XeroToken;
use Illuminate\Database\Eloquent\Factories\Factory;

class TokenFactory extends Factory
{
protected $model = XeroToken::class;

public function definition(): array
{
return [
'tenant_id' => $this->faker->uuid,
'tenant_name' => $this->faker->name,
'access_token' => $this->faker->uuid,
'refresh_token' => $this->faker->uuid,
'expires_in' => $this->faker->randomNumber(),
'created_at' => $this->faker->dateTime,
'updated_at' => $this->faker->dateTime,
'scopes' => config('xero.scopes'),
];
}
}
71 changes: 71 additions & 0 deletions tests/Actions/HandleTokenExpiredActionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

use Dcblogdev\Xero\Actions\tokenExpiredAction;
use Dcblogdev\Xero\Exceptions\XeroTokenExpiredException;
use Dcblogdev\Xero\Models\XeroToken;
use Illuminate\Support\Facades\Route;
use function Pest\Laravel\assertDatabaseCount;

test('token refresh throws exception when expired and a refresh is attempted over cli', function(){

$token = XeroToken::factory()->create();

$result = ['error' => 'invalid_grant'];

$action = new tokenExpiredAction();
$action->handle($result, $token);

assertDatabaseCount(XeroToken::class, 0);

})->throws(Exception::class, 'Xero token has expired, please re-authenticate.');

test('token refresh does not throw an exception and token is not deleted', function(){

$token = XeroToken::factory()->create();

$result = [];

$action = new tokenExpiredAction();
$response = $action->handle($result, $token);

expect($response)->toBeNull();

assertDatabaseCount(XeroToken::class, 1);

});

test('wip', function(){

$token = XeroToken::factory()->create();

$result = ['error' => 'invalid_grant'];

try {
$action = new TokenExpiredAction();
$action->handle($result, $token);
} catch (XeroTokenExpiredException $e) {
$this->assertEquals(config('xero.redirectUri'), $e->render()->getTargetUrl());
}


})->throws(XeroTokenExpiredException::class, 'Xero token has expired, please re-authenticate.');

test('token refresh redirects when expired and a refresh is attempted over HTTP', function () {

$this->withoutExceptionHandling();

$token = XeroToken::factory()->create();
assertDatabaseCount(XeroToken::class, 1);

// Define a temporary route in the test to handle the action.
Route::post('/test-endpoint', function () use ($token) {
$result = ['error' => 'invalid_grant'];
$action = new TokenExpiredAction();
$action->handle($result, $token);
});

$this->post('/test-endpoint')
->assertRedirect(config('xero.redirectUri'));

assertDatabaseCount(XeroToken::class, 0);
})->throws(XeroTokenExpiredException::class);

0 comments on commit 58299d4

Please sign in to comment.