diff --git a/README.md b/README.md index 8750c35..1998be5 100644 --- a/README.md +++ b/README.md @@ -19,56 +19,53 @@ $ composer require thecodingmachine/gotenberg-php-client ## Usage ```php -Foo'); + +$header = DocumentFactory::makeFromPath('header.html', '/path/to/file'); +$footer = DocumentFactory::makeFromPath('footer.html', '/path/to/file'); +$assets = [ + DocumentFactory::makeFromPath('style.css', '/path/to/file'), + DocumentFactory::makeFromPath('img.png', '/path/to/file'), +]; + +try { + $request = new HTMLRequest($index); + $request->setHeader($header); + $request->setFooter($footer); + $request->setAssets($assets); + $request->setPaperSize(Request::A4); + $request->setMargins(Request::NO_MARGINS); + + # store method allows you to... store the resulting PDF in a particular destination. + $client->store($request, 'path/you/want/the/pdf/to/be/stored.pdf'); - public function yourAwesomeMethod() - { - $client = new Client('http://localhost:3000', new \Http\Adapter\Guzzle6\Client()); - # or the following if you want the client to discover automatically an installed implementation of the PSR7 `HttpClient`. - $client = new Client('http://localhost:3000'); - - # HTML conversion example. - $index = DocumentFactory::makeFromPath('index.html', '/path/to/file'); - $header = DocumentFactory::makeFromPath('header.html', '/path/to/file'); - $footer = DocumentFactory::makeFromPath('footer.html', '/path/to/file'); - $assets = [ - DocumentFactory::makeFromPath('style.css', '/path/to/file'), - DocumentFactory::makeFromPath('img.png', '/path/to/file'), - ]; - - try { - $request = new HTMLRequest($index); - $request->setHeader($header); - $request->setFooter($footer); - $request->setAssets($assets); - $request->setPaperSize(Request::A4); - $request->setMargins(Request::NO_MARGINS); - - # store method allows you to... store the resulting PDF in a particular destination. - $client->store($request, 'path/you/want/the/pdf/to/be/stored.pdf'); - - # if you wish to redirect the response directly to the browser, you may also use: - $client->post($request); - - } catch (RequestException $e) { - # this exception is thrown if given paper size or margins are not correct. - } catch (ClientException $e) { - # this exception is thrown by the client if the API has returned a code != 200. - } catch (\Exception $e) { - # some (random?) exception. - } - } + # if you wish to redirect the response directly to the browser, you may also use: + $client->post($request); +} catch (RequestException $e) { + # this exception is thrown if given paper size or margins are not correct. +} catch (ClientException $e) { + # this exception is thrown by the client if the API has returned a code != 200. } ``` diff --git a/docker-compose.yml b/docker-compose.yml index f11c898..2ffb49c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,12 +6,15 @@ services: image: thecodingmachine/php:7.3-v2-cli container_name: php environment: - - PHP_EXTENSION_XDEBUG=1 + PHP_EXTENSION_XDEBUG: 1 restart: 'no' volumes: - ./:/usr/src/app:rw gotenberg: - image: thecodingmachine/gotenberg:6.0.1 + image: thecodingmachine/gotenberg:6 + environment: + LOG_LEVEL: 'DEBUG' + DEFAULT_WAIT_TIMEOUT: '30.0' container_name: gotenberg restart: 'no' \ No newline at end of file diff --git a/src/ChromeRequest.php b/src/ChromeRequest.php index b006a5b..3aec174 100644 --- a/src/ChromeRequest.php +++ b/src/ChromeRequest.php @@ -16,6 +16,7 @@ abstract class ChromeRequest extends Request implements GotenbergRequestInterfac private const MARGIN_LEFT = 'marginLeft'; private const MARGIN_RIGHT = 'marginRight'; private const LANDSCAPE = 'landscape'; + private const PAGE_RANGES = 'pageRanges'; private const GOOGLE_CHROME_RPCC_BUFFER_SIZE = 'googleChromeRpccBufferSize'; /** @var float|null */ @@ -48,6 +49,9 @@ abstract class ChromeRequest extends Request implements GotenbergRequestInterfac /** @var bool */ private $landscape; + /** @var string|null */ + private $pageRanges; + /** @var int|null */ private $googleChromeRpccBufferSize; @@ -78,6 +82,9 @@ public function getFormValues(): array if ($this->marginRight !== null) { $values[self::MARGIN_RIGHT] = $this->marginRight; } + if ($this->pageRanges !== null) { + $values[self::PAGE_RANGES] = $this->pageRanges; + } if ($this->googleChromeRpccBufferSize !== null) { $values[self::GOOGLE_CHROME_RPCC_BUFFER_SIZE] = $this->googleChromeRpccBufferSize; } @@ -182,6 +189,11 @@ public function setLandscape(bool $landscape): void $this->landscape = $landscape; } + public function setPageRanges(string $pageRanges): void + { + $this->pageRanges = $pageRanges; + } + public function setGoogleChromeRpccBufferSize(?int $googleChromeRpccBufferSize): void { $this->googleChromeRpccBufferSize = $googleChromeRpccBufferSize; diff --git a/src/Client.php b/src/Client.php index 7681ec1..69e1472 100644 --- a/src/Client.php +++ b/src/Client.php @@ -45,11 +45,15 @@ public function post(GotenbergRequestInterface $request): ResponseInterface * Sends the given documents to the API, stores the resulting PDF in the given destination. * * @throws ClientException + * @throws RequestException * @throws Exception * @throws FilesystemException */ public function store(GotenbergRequestInterface $request, string $destination): void { + if ($request->hasWebhook()) { + throw new RequestException('Cannot use method store with a webhook.'); + } $response = $this->handleResponse($this->client->sendRequest($this->makeMultipartFormDataRequest($request))); $fileStream = $response->getBody(); $fp = fopen($destination, 'w'); @@ -79,11 +83,15 @@ private function makeMultipartFormDataRequest(GotenbergRequestInterface $request } $body = new MultipartStream($multipartData); $messageFactory = MessageFactoryDiscovery::find(); - - return $messageFactory + $message = $messageFactory ->createRequest('POST', $this->apiURL . $request->getPostURL()) ->withHeader('Content-Type', 'multipart/form-data; boundary="' . $body->getBoundary() . '"') ->withBody($body); + foreach ($request->getCustomHTTPHeaders() as $key => $value) { + $message = $message->withHeader($key, $value); + } + + return $message; } /** diff --git a/src/GotenbergRequestInterface.php b/src/GotenbergRequestInterface.php index 9adea22..4991880 100644 --- a/src/GotenbergRequestInterface.php +++ b/src/GotenbergRequestInterface.php @@ -8,6 +8,11 @@ interface GotenbergRequestInterface { public function getPostURL(): string; + /** + * @return array + */ + public function getCustomHTTPHeaders(): array; + /** * @return array */ @@ -17,4 +22,6 @@ public function getFormValues(): array; * @return array */ public function getFormFiles(): array; + + public function hasWebhook(): bool; } diff --git a/src/HTMLRequest.php b/src/HTMLRequest.php index 77dec84..387d53f 100644 --- a/src/HTMLRequest.php +++ b/src/HTMLRequest.php @@ -14,6 +14,7 @@ class HTMLRequest extends ChromeRequest implements GotenbergRequestInterface public function __construct(Document $index) { + parent::__construct(); $this->index = $index; $this->assets = []; } diff --git a/src/MergeRequest.php b/src/MergeRequest.php index 89e00a8..a93017c 100644 --- a/src/MergeRequest.php +++ b/src/MergeRequest.php @@ -14,6 +14,7 @@ final class MergeRequest extends Request implements GotenbergRequestInterface */ public function __construct(array $files) { + parent::__construct(); $this->files = $files; } diff --git a/src/OfficeRequest.php b/src/OfficeRequest.php index d319c73..57c84d8 100644 --- a/src/OfficeRequest.php +++ b/src/OfficeRequest.php @@ -7,6 +7,7 @@ final class OfficeRequest extends Request implements GotenbergRequestInterface { private const LANDSCAPE = 'landscape'; + private const PAGE_RANGES = 'pageRanges'; /** @var Document[] */ private $files; @@ -14,11 +15,15 @@ final class OfficeRequest extends Request implements GotenbergRequestInterface /** @var bool */ private $landscape; + /** @var string|null */ + private $pageRanges; + /** * @param Document[] $files */ public function __construct(array $files) { + parent::__construct(); $this->files = $files; } @@ -33,6 +38,9 @@ public function getPostURL(): string public function getFormValues(): array { $values = parent::getFormValues(); + if ($this->pageRanges !== null) { + $values[self::PAGE_RANGES] = $this->pageRanges; + } $values[self::LANDSCAPE] = $this->landscape; return $values; @@ -55,4 +63,9 @@ public function setLandscape(bool $landscape): void { $this->landscape = $landscape; } + + public function setPageRanges(string $pageRanges): void + { + $this->pageRanges = $pageRanges; + } } diff --git a/src/Request.php b/src/Request.php index 01031a3..84afdef 100644 --- a/src/Request.php +++ b/src/Request.php @@ -23,6 +23,8 @@ abstract class Request private const WEBHOOK_URL = 'webhookURL'; private const WEBHOOK_URL_TIMEOUT = 'webhookURLTimeout'; + private const WEBHOOK_URL_BASE_HTTP_HEADER_KEY = 'Gotenberg-Webhookurl-'; + /** @var string|null */ private $resultFilename; @@ -35,6 +37,14 @@ abstract class Request /** @var float|null */ private $webhookURLTimeout; + /** @var array */ + protected $customHTTPHeaders; + + public function __construct() + { + $this->customHTTPHeaders = []; + } + /** * @return array */ @@ -57,6 +67,19 @@ public function getFormValues(): array return $values; } + public function hasWebhook(): bool + { + return ! empty($this->webhookURL); + } + + /** + * @return array + */ + public function getCustomHTTPHeaders(): array + { + return $this->customHTTPHeaders; + } + public function setResultFilename(?string $resultFilename): void { $this->resultFilename = $resultFilename; @@ -76,4 +99,10 @@ public function setWebhookURLTimeout(?float $webhookURLTimeout): void { $this->webhookURLTimeout = $webhookURLTimeout; } + + public function addWebhookURLHTTPHeader(string $key, string $value): void + { + $key = self::WEBHOOK_URL_BASE_HTTP_HEADER_KEY . $key; + $this->customHTTPHeaders[$key] = $value; + } } diff --git a/src/URLRequest.php b/src/URLRequest.php index fca96b6..f4ea84a 100644 --- a/src/URLRequest.php +++ b/src/URLRequest.php @@ -8,11 +8,14 @@ final class URLRequest extends ChromeRequest implements GotenbergRequestInterfac { private const REMOTE_URL = 'remoteURL'; + private const REMOTE_URL_BASE_HTTP_HEADER_KEY = 'Gotenberg-Remoteurl-'; + /** @var string */ private $URL; public function __construct(string $URL) { + parent::__construct(); $this->URL = $URL; } @@ -31,4 +34,10 @@ public function getFormValues(): array return $values; } + + public function addRemoteURLHTTPHeader(string $key, string $value): void + { + $key = self::REMOTE_URL_BASE_HTTP_HEADER_KEY . $key; + $this->customHTTPHeaders[$key] = $value; + } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 7a96698..e295c22 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -202,4 +202,64 @@ public function testStore(): void $client->store($this->mergeRequest, $filePath); $this->assertFileExists($filePath); } + + /** + * @throws ClientException + * @throws RequestException + */ + public function testPageRanges(): void + { + $client = new Client(self::API_URL, new \Http\Adapter\Guzzle6\Client()); + // case 1: HTML. + $request = $this->createHTMLRequest(); + $request->setPageRanges('1-1'); + $response = $client->post($request); + $this->assertEquals($response->getHeaderLine('Content-Type'), 'application/pdf'); + $this->assertNotEmpty($response->getBody()); + // case 2: URL. + $request = $this->createURLRequest(); + $request->setPageRanges('1-1'); + $response = $client->post($request); + $this->assertEquals($response->getHeaderLine('Content-Type'), 'application/pdf'); + $this->assertNotEmpty($response->getBody()); + // case 3: markdown. + $request = $this->createMarkdownRequest(); + $request->setPageRanges('1-1'); + $response = $client->post($request); + $this->assertEquals($response->getHeaderLine('Content-Type'), 'application/pdf'); + $this->assertNotEmpty($response->getBody()); + // case 4: office. + $request = $this->createOfficeRequest(); + $request->setPageRanges('1-1'); + $response = $client->post($request); + $this->assertEquals($response->getHeaderLine('Content-Type'), 'application/pdf'); + $this->assertNotEmpty($response->getBody()); + } + + /** + * @throws ClientException + */ + public function testWebhook(): void + { + $client = new Client(self::API_URL, new \Http\Adapter\Guzzle6\Client()); + $request = $this->createMergeRequest(); + $request->setWebhookURL('https://google.com'); + $request->setWebhookURLTimeout(5.0); + $request->addWebhookURLHTTPHeader('A-Header', 'Foo'); + $response = $client->post($request); + $this->assertEquals(200, $response->getStatusCode()); + } + + /** + * @throws RequestException + * @throws ClientException + */ + public function testRemoteURLHTTPHeader(): void + { + $client = new Client(self::API_URL, new \Http\Adapter\Guzzle6\Client()); + $request = $this->createURLRequest(); + $request->addRemoteURLHTTPHeader('A-Header', 'Foo'); + $response = $client->post($request); + $this->assertEquals(200, $response->getStatusCode()); + } } diff --git a/tests/DocumentFactoryTest.php b/tests/DocumentFactoryTest.php index 69248c8..83f282b 100644 --- a/tests/DocumentFactoryTest.php +++ b/tests/DocumentFactoryTest.php @@ -6,9 +6,13 @@ use GuzzleHttp\Psr7\LazyOpenStream; use PHPUnit\Framework\TestCase; +use Safe\Exceptions\FilesystemException; final class DocumentFactoryTest extends TestCase { + /** + * @throws FilesystemException + */ public function testMake(): void { // case 1: uses a file path.