Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update configuration #24

Merged
merged 7 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 5 additions & 56 deletions src/Builder/AbstractChromiumPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface;
use Sensiolabs\GotenbergBundle\Enum\PdfPart;
use Sensiolabs\GotenbergBundle\Exception\ExtraHttpHeadersJsonEncodingException;
use Sensiolabs\GotenbergBundle\Exception\InvalidBuilderConfiguration;
use Sensiolabs\GotenbergBundle\Exception\PdfPartRenderingException;
use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter;
Expand Down Expand Up @@ -194,7 +193,7 @@ public function nativePageRanges(string $range): static
}

/**
* @param string $template #Template
* @param string $template #Template
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CS fixer won't be happy but 👍 for now. I'll fix it in my "add CI" PR .

* @param array<string, mixed> $context
*
* @throws PdfPartRenderingException if the template could not be rendered
Expand All @@ -205,7 +204,7 @@ public function header(string $template, array $context = []): static
}

/**
* @param string $template #Template
* @param string $template #Template
* @param array<string, mixed> $context
*
* @throws PdfPartRenderingException if the template could not be rendered
Expand Down Expand Up @@ -313,7 +312,7 @@ public function userAgent(string $userAgent): static

/**
* Sets extra HTTP headers that Chromium will send when loading the HTML
* document. (default None).
* document. (default None). (overrides any previous headers).
*
* @see https://gotenberg.dev/docs/routes#custom-http-headers
*
Expand All @@ -336,10 +335,7 @@ public function extraHttpHeaders(array $headers): static
*/
public function addExtraHttpHeaders(array $headers): static
{
$this->formFields['extraHttpHeaders'] = [
...$this->formFields['extraHttpHeaders'],
...$headers,
];
$this->formFields['extraHttpHeaders'] = array_merge($this->formFields['extraHttpHeaders'] ?? [], $headers);

return $this;
}
Expand Down Expand Up @@ -381,53 +377,6 @@ public function pdfUniversalAccess(bool $bool = true): static
return $this;
}

/**
* @throws ExtraHttpHeadersJsonEncodingException
*/
public function getMultipartFormData(): array
{
$formFields = $this->formFields;
$multipartFormData = [];

$extraHttpHeaders = $this->formFields['extraHttpHeaders'] ?? [];
if ([] !== $extraHttpHeaders) {
try {
$extraHttpHeaders = json_encode($extraHttpHeaders, \JSON_THROW_ON_ERROR);
} catch (\JsonException $exception) {
throw new ExtraHttpHeadersJsonEncodingException('Could not encode extra HTTP headers into JSON', previous: $exception);
}

$multipartFormData[] = [
'extraHttpHeaders' => $extraHttpHeaders,
];
unset($formFields['extraHttpHeaders']);
}

foreach ($formFields as $key => $value) {
if (\is_bool($value)) {
$multipartFormData[] = [
$key => $value ? 'true' : 'false',
];
continue;
}

if (\is_array($value)) {
foreach ($value as $nestedValue) {
$multipartFormData[] = [
($nestedValue instanceof DataPart ? 'files' : $key) => $nestedValue,
];
}
continue;
}

$multipartFormData[] = [
($value instanceof DataPart ? 'files' : $key) => $value,
];
}

return $multipartFormData;
}

protected function withPdfPartFile(PdfPart $pdfPart, string $path): static
{
$dataPart = new DataPart(
Expand All @@ -441,7 +390,7 @@ protected function withPdfPartFile(PdfPart $pdfPart, string $path): static
}

/**
* @param string $template #Template
* @param string $template #Template
* @param array<string, mixed> $context
*
* @throws PdfPartRenderingException if the template could not be rendered
Expand Down
111 changes: 103 additions & 8 deletions src/Builder/AbstractPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@

use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface;
use Sensiolabs\GotenbergBundle\Client\PdfResponse;
use Sensiolabs\GotenbergBundle\Enum\PdfPart;
use Sensiolabs\GotenbergBundle\Exception\ExtraHttpHeadersJsonEncodingException;
use Sensiolabs\GotenbergBundle\Formatter\AssetBaseDirFormatter;
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\HeaderUtils;
use Symfony\Component\Mime\Part\DataPart;

abstract class AbstractPdfBuilder implements PdfBuilderInterface
{
Expand All @@ -19,19 +22,40 @@ abstract class AbstractPdfBuilder implements PdfBuilderInterface

private string $headerDisposition = HeaderUtils::DISPOSITION_INLINE;

/**
* @var array<string, (\Closure(mixed): array<string, array<string|int ,mixed>|non-empty-string|int|float|bool|DataPart>)>
*/
private array $normalizers;

public function __construct(
protected readonly GotenbergClientInterface $gotenbergClient,
protected readonly AssetBaseDirFormatter $asset,
) {
$this->normalizers = [
'extraHttpHeaders' => static function (mixed $value): array {
try {
$extraHttpHeaders = json_encode($value, \JSON_THROW_ON_ERROR);
} catch (\JsonException $exception) {
throw new ExtraHttpHeadersJsonEncodingException('Could not encode extra HTTP headers into JSON', previous: $exception);
}

return ['extraHttpHeaders' => $extraHttpHeaders];
},
'assets' => static function (array $value): array {
return ['files' => $value];
},
PdfPart::HeaderPart->value => static function (DataPart $value): array {
return ['files' => $value];
},
PdfPart::BodyPart->value => static function (DataPart $value): array {
return ['files' => $value];
},
PdfPart::FooterPart->value => static function (DataPart $value): array {
return ['files' => $value];
},
];
}

/**
* Compiles the form values into a multipart form data array to send to the HTTP client.
*
* @return list<array<string, string>>
*/
abstract public function getMultipartFormData(): array;

/**
* The Gotenberg API endpoint path.
*/
Expand Down Expand Up @@ -60,7 +84,7 @@ public function generate(): PdfResponse
if (null !== $this->fileName) {
$disposition = HeaderUtils::makeDisposition(
$this->headerDisposition,
$this->fileName
$this->fileName,
);

$pdfResponse
Expand All @@ -71,6 +95,77 @@ public function generate(): PdfResponse
return $pdfResponse;
}

/**
* Compiles the form values into a multipart form data array to send to the HTTP client.
*
* @return array<int, array<string, string>>
*/
public function getMultipartFormData(): array
{
$multipartFormData = [];

foreach ($this->formFields as $key => $value) {
$preCallback = null;

if (\array_key_exists($key, $this->normalizers)) {
$preCallback = $this->normalizers[$key](...);
}

foreach ($this->addToMultipart($key, $value, $preCallback) as $multiPart) {
$multipartFormData[] = $multiPart;
}
}

return $multipartFormData;
}

protected function addNormalizer(string $key, \Closure $normalizer): void
{
$this->normalizers[$key] = $normalizer;
}

/**
* @param array<int|string, mixed>|string|int|float|bool|DataPart $value
*
* @return list<array<string, mixed>>
*/
private function addToMultipart(string $key, array|string|int|float|bool|DataPart $value, \Closure|null $preCallback = null): array
{
if (null !== $preCallback) {
$result = [];
foreach ($preCallback($value) as $key => $value) {
$result[] = $this->addToMultipart($key, $value);

return array_merge(...$result);
}
}

if (\is_bool($value)) {
return [[
$key => $value ? 'true' : 'false',
]];
}

if (\is_int($value) || \is_float($value)) {
return [[
$key => (string) $value,
]];
}

if (\is_array($value)) {
$result = [];
foreach ($value as $nestedValue) {
$result[] = $this->addToMultipart($key, $nestedValue);
}

return array_merge(...$result);
}

return [[
$key => $value,
]];
}

/**
* @param non-empty-list<string> $validExtensions
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Builder/HtmlPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class HtmlPdfBuilder extends AbstractChromiumPdfBuilder
private const ENDPOINT = '/forms/chromium/convert/html';

/**
* @param string $template #Template
* @param string $template #Template
* @param array<string, mixed> $context
*
* @throws PdfPartRenderingException if the template could not be rendered
Expand Down
28 changes: 1 addition & 27 deletions src/Builder/LibreOfficePdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,33 +110,7 @@ public function getMultipartFormData(): array
throw new MissingRequiredFieldException('At least one office file is required');
}

$formFields = $this->formFields;
$multipartFormData = [];

$files = $this->formFields['files'] ?? [];
if ([] !== $files) {
foreach ($files as $dataPart) {
$multipartFormData[] = [
'files' => $dataPart,
];
}
unset($formFields['files']);
}

foreach ($formFields as $key => $value) {
if (\is_bool($value)) {
$multipartFormData[] = [
$key => $value ? 'true' : 'false',
];
continue;
}

$multipartFormData[] = [
$key => $value,
];
}

return $multipartFormData;
return parent::getMultipartFormData();
}

protected function getEndpoint(): string
Expand Down
2 changes: 1 addition & 1 deletion src/Builder/MarkdownPdfBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class MarkdownPdfBuilder extends AbstractChromiumPdfBuilder
/**
* The HTML file that wraps the markdown content, rendered from a Twig template.
*
* @param string $template #Template
* @param string $template #Template
* @param array<string, mixed> $context
*
* @throws PdfPartRenderingException if the template could not be rendered
Expand Down
3 changes: 3 additions & 0 deletions src/Pdf/GotenbergInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ interface GotenbergInterface
public function get(string $builder): PdfBuilderInterface;

public function html(): HtmlPdfBuilder;

public function url(): UrlPdfBuilder;

public function office(): LibreOfficePdfBuilder;

public function markdown(): MarkdownPdfBuilder;
}
46 changes: 23 additions & 23 deletions tests/Builder/HtmlPdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,32 +30,32 @@ public function testWithConfigurations(): void

self::assertCount(21, $multipartFormData);

self::assertSame(['extraHttpHeaders' => '{"MyHeader":"Value","User-Agent":"MyValue"}'], $multipartFormData[0]);
self::assertSame(['paperWidth' => 33.1], $multipartFormData[2]);
self::assertSame(['paperHeight' => 46.8], $multipartFormData[3]);
self::assertSame(['marginTop' => 1.0], $multipartFormData[4]);
self::assertSame(['marginBottom' => 1.0], $multipartFormData[5]);
self::assertSame(['marginLeft' => 1.0], $multipartFormData[6]);
self::assertSame(['marginRight' => 1.0], $multipartFormData[7]);
self::assertSame(['preferCssPageSize' => 'true'], $multipartFormData[8]);
self::assertSame(['printBackground' => 'true'], $multipartFormData[9]);
self::assertSame(['omitBackground' => 'true'], $multipartFormData[10]);
self::assertSame(['landscape' => 'true'], $multipartFormData[11]);
self::assertSame(['scale' => 1.5], $multipartFormData[12]);
self::assertSame(['nativePageRanges' => '1-5'], $multipartFormData[13]);
self::assertSame(['waitDelay' => '10s'], $multipartFormData[14]);
self::assertSame(['waitForExpression' => 'window.globalVar === "ready"'], $multipartFormData[15]);
self::assertSame(['emulatedMediaType' => 'screen'], $multipartFormData[16]);
self::assertSame(['userAgent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML => like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'], $multipartFormData[17]);
self::assertIsArray($multipartFormData[0]);
self::assertCount(1, $multipartFormData[0]);
self::assertArrayHasKey('files', $multipartFormData[0]);
self::assertInstanceOf(DataPart::class, $multipartFormData[0]['files']);
self::assertSame('index.html', $multipartFormData[0]['files']->getFilename());

self::assertSame(['paperWidth' => '33.1'], $multipartFormData[1]);
self::assertSame(['paperHeight' => '46.8'], $multipartFormData[2]);
self::assertSame(['marginTop' => '1'], $multipartFormData[3]);
self::assertSame(['marginBottom' => '1'], $multipartFormData[4]);
self::assertSame(['marginLeft' => '1'], $multipartFormData[5]);
self::assertSame(['marginRight' => '1'], $multipartFormData[6]);
self::assertSame(['preferCssPageSize' => 'true'], $multipartFormData[7]);
self::assertSame(['printBackground' => 'true'], $multipartFormData[8]);
self::assertSame(['omitBackground' => 'true'], $multipartFormData[9]);
self::assertSame(['landscape' => 'true'], $multipartFormData[10]);
self::assertSame(['scale' => '1.5'], $multipartFormData[11]);
self::assertSame(['nativePageRanges' => '1-5'], $multipartFormData[12]);
self::assertSame(['waitDelay' => '10s'], $multipartFormData[13]);
self::assertSame(['waitForExpression' => 'window.globalVar === "ready"'], $multipartFormData[14]);
self::assertSame(['emulatedMediaType' => 'screen'], $multipartFormData[15]);
self::assertSame(['userAgent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML => like Gecko) Version/11.0 Mobile/15A372 Safari/604.1'], $multipartFormData[16]);
self::assertSame(['extraHttpHeaders' => '{"MyHeader":"Value","User-Agent":"MyValue"}'], $multipartFormData[17]);
self::assertSame(['failOnConsoleExceptions' => 'true'], $multipartFormData[18]);
self::assertSame(['pdfa' => 'PDF/A-1a'], $multipartFormData[19]);
self::assertSame(['pdfua' => 'true'], $multipartFormData[20]);

self::assertIsArray($multipartFormData[1]);
self::assertCount(1, $multipartFormData[1]);
self::assertArrayHasKey('files', $multipartFormData[1]);
self::assertInstanceOf(DataPart::class, $multipartFormData[1]['files']);
self::assertSame('index.html', $multipartFormData[1]['files']->getFilename());
}

public function testWithTemplate(): void
Expand Down
Loading