-
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ Webhook Callbacks endpoint CRUD methods
Adding callbacks resource for registering and managing webhooks. This provides the basic CRUD operations. Verification and resending methods to follow. See Issue#44
- Loading branch information
Showing
8 changed files
with
350 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace amcintosh\FreshBooks\Model; | ||
|
||
use DateTimeImmutable; | ||
use Spatie\DataTransferObject\Attributes\CastWith; | ||
use Spatie\DataTransferObject\Attributes\MapFrom; | ||
use Spatie\DataTransferObject\Attributes\MapTo; | ||
use Spatie\DataTransferObject\Caster; | ||
use Spatie\DataTransferObject\DataTransferObject; | ||
use amcintosh\FreshBooks\Model\Caster\ISODateTimeImmutableCaster; | ||
use amcintosh\FreshBooks\Model\DataModel; | ||
|
||
/** | ||
* Webhook callback subscription model. | ||
* | ||
* @package amcintosh\FreshBooks\Model | ||
* @link https://www.freshbooks.com/api/webhooks | ||
*/ | ||
class Callback extends DataTransferObject implements DataModel | ||
{ | ||
public const RESPONSE_FIELD = 'callback'; | ||
|
||
/** | ||
* @var int Get the unique identifier of this callback within this business. | ||
*/ | ||
#[MapFrom('callbackid')] | ||
public ?int $callbackId; | ||
|
||
/** | ||
* @var string The event to register the webhook callback for (eg. `invoice.create`). | ||
*/ | ||
public ?string $event; | ||
|
||
/** | ||
* @var DateTimeImmutable The time of last modification. | ||
*/ | ||
#[MapFrom('updated_at')] | ||
#[CastWith(ISODateTimeImmutableCaster::class)] | ||
public ?DateTimeImmutable $updatedAt; | ||
|
||
/** | ||
* @var string The URI to send the webhook callback to. | ||
*/ | ||
public ?string $uri; | ||
|
||
/** | ||
* @var bool Whether the callback has been verified against the URI. | ||
*/ | ||
public ?bool $verified; | ||
|
||
/** | ||
* Get the data as an array to POST or PUT to FreshBooks, removing any read-only fields. | ||
* | ||
* @return array | ||
*/ | ||
public function getContent(): array | ||
{ | ||
$data = $this | ||
->except('callbackId') | ||
->except('updatedAt') | ||
->except('verified') | ||
->toArray(); | ||
foreach ($data as $key => $value) { | ||
if (is_null($value)) { | ||
unset($data[$key]); | ||
} | ||
} | ||
return $data; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace amcintosh\FreshBooks\Model; | ||
|
||
use Spatie\DataTransferObject\Attributes\CastWith; | ||
use Spatie\DataTransferObject\Casters\ArrayCaster; | ||
use amcintosh\FreshBooks\Model\AccountingList; | ||
use amcintosh\FreshBooks\Model\Callback; | ||
|
||
/** | ||
* Results of callbacks list call containing list of callbacks and pagination data. | ||
* | ||
* @package amcintosh\FreshBooks\Model | ||
* @link https://www.freshbooks.com/api/webhooks | ||
*/ | ||
class CallbackList extends AccountingList | ||
{ | ||
public const RESPONSE_FIELD = 'callbacks'; | ||
|
||
#[CastWith(ArrayCaster::class, itemType: Callback::class)] | ||
public array $callbacks; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace amcintosh\FreshBooks\Resource; | ||
|
||
use Http\Client\HttpClient; | ||
use Spatie\DataTransferObject\DataTransferObject; | ||
use amcintosh\FreshBooks\Builder\IncludesBuilder; | ||
use amcintosh\FreshBooks\Exception\FreshBooksException; | ||
use amcintosh\FreshBooks\Exception\FreshBooksNotImplementedException; | ||
use amcintosh\FreshBooks\Model\DataModel; | ||
use amcintosh\FreshBooks\Model\ListModel; | ||
use amcintosh\FreshBooks\Model\VisState; | ||
|
||
/** | ||
* Resource for calls to /events endpoints. | ||
* | ||
* @package amcintosh\FreshBooks\Resource | ||
*/ | ||
class EventsResource extends AccountingResource | ||
{ | ||
/** | ||
* The the url to the events resource. | ||
* | ||
* @param string $accountId | ||
* @param int $resourceId | ||
* @return string | ||
*/ | ||
protected function getUrl(string $accountId, int $resourceId = null): string | ||
{ | ||
if (!is_null($resourceId)) { | ||
return "/events/account/{$accountId}/{$this->accountingPath}/{$resourceId}"; | ||
} | ||
return "/events/account/{$accountId}/{$this->accountingPath}"; | ||
} | ||
|
||
/** | ||
* Create a FreshBooksException from the json response from the events endpoint. | ||
* | ||
* @param int $statusCode HTTP status code | ||
* @param array $responseData The json-parsed response | ||
* @param string $rawRespone The raw response body | ||
* @return void | ||
*/ | ||
protected function handleError(int $statusCode, array $responseData, string $rawRespone): void | ||
{ | ||
if (!array_key_exists('message', $responseData) || !array_key_exists('code', $responseData)) { | ||
throw new FreshBooksException('Unknown error', $statusCode, null, $rawRespone); | ||
} | ||
|
||
$message = $responseData['message']; | ||
$errorCode = null; | ||
$details = []; | ||
|
||
foreach ($responseData['details'] as $detail) { | ||
if ( | ||
in_array('type.googleapis.com/google.rpc.BadRequest', $detail) | ||
&& array_key_exists('fieldViolations', $detail) | ||
) { | ||
$details = $detail['fieldViolations']; | ||
} | ||
} | ||
var_dump($details); | ||
throw new FreshBooksException($message, $statusCode, null, $rawRespone, $errorCode, $details); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace amcintosh\FreshBooks\Tests\Model; | ||
|
||
use DateTime; | ||
use PHPUnit\Framework\TestCase; | ||
use amcintosh\FreshBooks\Model\Callback; | ||
|
||
final class CallbackTest extends TestCase | ||
{ | ||
private $sampleCallbackData = '{"callback":{ | ||
"callbackid": 123, | ||
"verified": true, | ||
"uri": "http://freshbooks.com/hook/123", | ||
"event": "invoice.create", | ||
"updated_at": "2017-08-23T11:45:09Z"}}'; | ||
|
||
public function testCallbackFromResponse(): void | ||
{ | ||
$callbackData = json_decode($this->sampleCallbackData, true); | ||
|
||
$callback = new Callback($callbackData[Callback::RESPONSE_FIELD]); | ||
|
||
$this->assertSame(123, $callback->callbackId); | ||
$this->assertSame('invoice.create', $callback->event); | ||
$this->assertEquals(new DateTime('2017-08-23T11:45:09Z'), $callback->updatedAt); | ||
$this->assertSame('http://freshbooks.com/hook/123', $callback->uri); | ||
$this->assertTrue($callback->verified); | ||
} | ||
|
||
public function testCallbackGetContent(): void | ||
{ | ||
$callbackData = json_decode($this->sampleCallbackData, true); | ||
$callback = new Callback($callbackData['callback']); | ||
$this->assertSame([ | ||
'event' => 'invoice.create', | ||
'uri' => 'http://freshbooks.com/hook/123' | ||
], $callback->getContent()); | ||
} | ||
} |
Oops, something went wrong.