Skip to content

Commit

Permalink
Add all configurations for HtmlPdfBuilder
Browse files Browse the repository at this point in the history
  • Loading branch information
StevenRenaux committed Jan 31, 2025
1 parent 3f8f170 commit c870e8e
Show file tree
Hide file tree
Showing 55 changed files with 1,340 additions and 1,403 deletions.
12 changes: 0 additions & 12 deletions config/builder_pdf.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,29 +23,17 @@
;

// HTML
$services->set('.sensiolabs_gotenberg.pdf_builder_configurator.html', HtmlPdfBuilderConfigurator::class)
->args([
abstract_arg('default configuration'),
])
;
$services->set('.sensiolabs_gotenberg.pdf_builder.html', HtmlPdfBuilder::class)
->share(false)
->parent('.sensiolabs_gotenberg.abstract_builder')
->configurator(service('.sensiolabs_gotenberg.pdf_builder_configurator.html'))
->tag('sensiolabs_gotenberg.builder')
->tag('sensiolabs_gotenberg.pdf_builder')
;

// Merge
$services->set('.sensiolabs_gotenberg.pdf_builder_configurator.merge', MergePdfBuilderConfigurator::class)
->args([
abstract_arg('default configuration'),
])
;
$services->set('.sensiolabs_gotenberg.pdf_builder.merge', MergePdfBuilder::class)
->share(false)
->parent('.sensiolabs_gotenberg.abstract_builder')
->configurator(service('.sensiolabs_gotenberg.pdf_builder_configurator.merge'))
->tag('sensiolabs_gotenberg.pdf_builder')
;

Expand Down
6 changes: 0 additions & 6 deletions config/builder_screenshot.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,9 @@
;

// HTML
$services->set('.sensiolabs_gotenberg.screenshot_builder_configurator.html', HtmlScreenshotBuilderConfigurator::class)
->args([
abstract_arg('default configuration'),
])
;
$services->set('.sensiolabs_gotenberg.screenshot_builder.html', HtmlScreenshotBuilder::class)
->share(false)
->parent('.sensiolabs_gotenberg.abstract_builder')
->configurator(service('.sensiolabs_gotenberg.screenshot_builder_configurator.html'))
->tag('sensiolabs_gotenberg.builder')
->tag('sensiolabs_gotenberg.screenshot_builder')
;
Expand Down
1 change: 1 addition & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<exclude>tests/Builder/AsyncBuilderTraitTest.php</exclude>
<exclude>tests/GotenbergScreenshotTest.php</exclude>
<exclude>tests/DependencyInjection/CompilerPass/GotenbergPassTest.php</exclude>
<exclude>tests/Builder/Pdf/MergePdfBuilderTest.php</exclude>
</testsuite>
</testsuites>

Expand Down
96 changes: 72 additions & 24 deletions src/Builder/AbstractBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

use Psr\Container\ContainerInterface;
use Sensiolabs\GotenbergBundle\Builder\Attributes\ExposeSemantic;
use Sensiolabs\GotenbergBundle\Builder\Attributes\NormalizeGotenbergPayload;
use Sensiolabs\GotenbergBundle\Builder\Attributes\SemanticNode;
use Sensiolabs\GotenbergBundle\Builder\Pdf\HtmlPdfBuilder;
use Sensiolabs\GotenbergBundle\Builder\Result\GotenbergAsyncResult;
use Sensiolabs\GotenbergBundle\Builder\Result\GotenbergFileResult;
use Sensiolabs\GotenbergBundle\Client\GotenbergClientInterface;
use Sensiolabs\GotenbergBundle\Configurator\NodeBuilderDispatcher;
use Sensiolabs\GotenbergBundle\PayloadResolver\Payload;
use Sensiolabs\GotenbergBundle\NodeBuilder\NodeBuilderDispatcher;
use Sensiolabs\GotenbergBundle\Processor\NullProcessor;
use Sensiolabs\GotenbergBundle\Processor\ProcessorInterface;
use Symfony\Component\Config\Definition\Builder\NodeDefinition;
Expand All @@ -23,6 +25,8 @@ abstract class AbstractBuilder implements BuilderAsyncInterface, BuilderFileInte
private readonly BodyBag $bodyBag;
private readonly HeadersBag $headersBag;

private readonly \ReflectionClass $reflection;

private string $headerDisposition = HeaderUtils::DISPOSITION_INLINE;

/** @var ProcessorInterface<mixed>|null */
Expand All @@ -34,18 +38,22 @@ public function __construct(
) {
$this->bodyBag = new BodyBag();
$this->headersBag = new HeadersBag();

$this->reflection = new \ReflectionClass(static::class);
}

abstract protected function getEndpoint(): string;

abstract protected function normalize(): \Generator;

public static function getConfiguration(): NodeDefinition
{
$treeBuilder = new TreeBuilder('html');
$root = $treeBuilder->getRootNode()->addDefaultsIfNotSet();

$reflection = new \ReflectionClass(static::class);
$nodeAttributes = $reflection->getAttributes(SemanticNode::class);

/** @var SemanticNode $attribute */
$semanticNode = $nodeAttributes[0]->newInstance();

$treeBuilder = new TreeBuilder($semanticNode->name);
$root = $treeBuilder->getRootNode()->addDefaultsIfNotSet();

foreach (array_reverse($reflection->getMethods(\ReflectionMethod::IS_PUBLIC)) as $methodR) {
$attributes = $methodR->getAttributes(ExposeSemantic::class);
Expand All @@ -58,9 +66,41 @@ public static function getConfiguration(): NodeDefinition
$root->append(NodeBuilderDispatcher::getNode($attribute));
}

if (HtmlPdfBuilder::class === static::class) {
$root->validate()->ifTrue(function ($v): bool {
return isset($v['paper_standard_size']) && (isset($v['paper_height']) || isset($v['paper_width']));
})->thenInvalid('You cannot use "paper_standard_size" when "paper_height", "paper_width" or both are set".');
}

return $root;
}

/**
* To set configurations by an array of configurations.
*
* @param array<string, mixed> $configurations
*/
public function setConfigurations(array $configurations): static
{
foreach (array_reverse($this->reflection->getMethods(\ReflectionMethod::IS_PUBLIC)) as $methodR) {
$attributes = $methodR->getAttributes(ExposeSemantic::class);
if (\count($attributes) === 0) {
continue;
}

/** @var ExposeSemantic $attribute */
$attribute = $attributes[0]->newInstance();

if (!\array_key_exists($attribute->name, $configurations)) {
continue;
}

$this->{$methodR->getName()}($configurations[$attribute->name]);
}

return $this;
}

/**
* @see https://gotenberg.dev/docs/routes#output-filename.
*
Expand All @@ -87,8 +127,8 @@ public function processor(ProcessorInterface $processor): static

public function generate(): GotenbergFileResult
{
$this->validatePayload();
$payloadBody = iterator_to_array($this->resolvePayloadBody());
$this->validatePayloadBody();
$payloadBody = iterator_to_array($this->normalizePayloadBody());

$response = $this->client->call(
$this->getEndpoint(),
Expand All @@ -108,8 +148,8 @@ public function generate(): GotenbergFileResult

public function generateAsync(): GotenbergAsyncResult
{
$this->validatePayload();
$payloadBody = iterator_to_array($this->resolvePayloadBody());
$this->validatePayloadBody();
$payloadBody = iterator_to_array($this->normalizePayloadBody());

$response = $this->client->call(
$this->getEndpoint(),
Expand All @@ -134,28 +174,36 @@ public function getHeadersBag(): HeadersBag
return $this->headersBag;
}

protected function validatePayload(): void
protected function validatePayloadBody(): void
{
}

private function resolvePayloadBody(): \Generator
private function normalizePayloadBody(): \Generator
{
foreach ($this->normalize() as $key => $normalizer) {
if ($this->getBodyBag()->get($key) === null) {
foreach (array_reverse($this->reflection->getMethods(\ReflectionMethod::IS_PROTECTED)) as $methodR) {
$attributes = $methodR->getAttributes(NormalizeGotenbergPayload::class);

if (\count($attributes) === 0) {
continue;
}

if (!\is_callable($normalizer)) {
throw new \RuntimeException(\sprintf('Le normalizer pour "%s" n\'est pas une fonction valide.', $key));
}
foreach ($this->{$methodR->getName()}() as $key => $normalizer) {
if ($this->getBodyBag()->get($key) === null) {
continue;
}

if (!\is_callable($normalizer)) {
throw new \RuntimeException(\sprintf('Normalizer "%s" is not a valid callable function.', $key));
}

if ('assets' === $key && \count($this->getBodyBag()->get($key)) > 1) {
$multipleFiles = $normalizer($key, $this->getBodyBag()->get($key));
foreach ($multipleFiles as $file) {
yield $file;
if ('assets' === $key && \count($this->getBodyBag()->get($key)) > 1) {
$multipleFiles = $normalizer($key, $this->getBodyBag()->get($key));
foreach ($multipleFiles as $file) {
yield $file;
}
} else {
yield $normalizer($key, $this->getBodyBag()->get($key));
}
} else {
yield $normalizer($key, $this->getBodyBag()->get($key));
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/Builder/Attributes/NormalizeGotenbergPayload.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Sensiolabs\GotenbergBundle\Builder\Attributes;

#[\Attribute(\Attribute::TARGET_METHOD)]
final class NormalizeGotenbergPayload
{
public function __construct()
{
}
}
14 changes: 14 additions & 0 deletions src/Builder/Attributes/SemanticNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

namespace Sensiolabs\GotenbergBundle\Builder\Attributes;

use Sensiolabs\GotenbergBundle\Enumeration\NodeType;

#[\Attribute(\Attribute::TARGET_CLASS)]
final class SemanticNode
{
public function __construct(
public readonly string $name,
) {
}
}
6 changes: 4 additions & 2 deletions src/Builder/Behaviors/Chromium/AssetTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

namespace Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium;

use Sensiolabs\GotenbergBundle\Builder\Attributes\NormalizeGotenbergPayload;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Dependencies\AssetBaseDirFormatterAwareTrait;
use Sensiolabs\GotenbergBundle\Builder\BodyBag;
use Sensiolabs\GotenbergBundle\PayloadResolver\Util\NormalizerFactory;
use Sensiolabs\GotenbergBundle\Builder\Util\NormalizerFactory;

/**
* See https://gotenberg.dev/docs/routes#html-file-into-pdf-route.
Expand Down Expand Up @@ -47,7 +48,8 @@ public function addAsset(string $path): static
return $this;
}

public function normalize(): \Generator
#[NormalizeGotenbergPayload]
protected function normalizeAsset(): \Generator
{
yield 'assets' => NormalizerFactory::asset();
}
Expand Down
7 changes: 4 additions & 3 deletions src/Builder/Behaviors/Chromium/ContentTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

namespace Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium;

use Sensiolabs\GotenbergBundle\Builder\Attributes\NormalizeGotenbergPayload;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Dependencies\AssetBaseDirFormatterAwareTrait;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Dependencies\TwigAwareTrait;
use Sensiolabs\GotenbergBundle\Builder\BodyBag;
use Sensiolabs\GotenbergBundle\Builder\Configuration;
use Sensiolabs\GotenbergBundle\Builder\Util\NormalizerFactory;
use Sensiolabs\GotenbergBundle\Builder\ValueObject\RenderedPart;
use Sensiolabs\GotenbergBundle\Enumeration\Part;
use Sensiolabs\GotenbergBundle\Exception\PdfPartRenderingException;
use Sensiolabs\GotenbergBundle\PayloadResolver\Util\NormalizerFactory;
use Sensiolabs\GotenbergBundle\Twig\GotenbergAssetRuntime;

trait ContentTrait
Expand Down Expand Up @@ -109,7 +109,8 @@ protected function withFilePart(Part $part, string $path): static
return $this;
}

protected function normalize(): \Generator
#[NormalizeGotenbergPayload]
protected function normalizeContent(): \Generator
{
yield 'header.html' => NormalizerFactory::content();
yield 'index.html' => NormalizerFactory::content();
Expand Down
25 changes: 25 additions & 0 deletions src/Builder/Behaviors/Chromium/CookieTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@

namespace Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium;

use Sensiolabs\GotenbergBundle\Builder\Attributes\ExposeSemantic;
use Sensiolabs\GotenbergBundle\Builder\Attributes\NormalizeGotenbergPayload;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Dependencies\LoggerAwareTrait;
use Sensiolabs\GotenbergBundle\Builder\Behaviors\Dependencies\RequestAwareTrait;
use Sensiolabs\GotenbergBundle\Builder\BodyBag;
use Sensiolabs\GotenbergBundle\Builder\Util\NormalizerFactory;
use Sensiolabs\GotenbergBundle\Builder\Util\ValidatorFactory;
use Sensiolabs\GotenbergBundle\Enumeration\NodeType;
use Sensiolabs\GotenbergBundle\Exception\InvalidBuilderConfiguration;
use Symfony\Component\HttpFoundation\Cookie;

/**
Expand All @@ -20,6 +26,15 @@ abstract protected function getBodyBag(): BodyBag;
/**
* @param list<Cookie|array{name: string, value: string, domain: string, path?: string|null, secure?: bool|null, httpOnly?: bool|null, sameSite?: 'Strict'|'Lax'|null}> $cookies
*/
#[ExposeSemantic('cookies', NodeType::Array, ['default_value' => [], 'prototype' => 'array', 'children' => [
['name' => 'name', 'options' => ['required' => true]],
['name' => 'value', 'options' => ['required' => true]],
['name' => 'domain', 'options' => ['required' => true]],
['name' => 'path'],
['name' => 'secure', 'node_type' => NodeType::Boolean],
['name' => 'httpOnly', 'node_type' => NodeType::Boolean],
['name' => 'sameSite', 'node_type' => NodeType::Enum, 'options' => ['values' => ['Strict', 'Lax', 'None']]],
]])]
public function cookies(array $cookies): static
{
if ([] === $cookies) {
Expand All @@ -42,6 +57,10 @@ public function cookies(array $cookies): static
*/
public function addCookies(array $cookies): static
{
if (!ValidatorFactory::cookies($cookies)) {
throw new InvalidBuilderConfiguration('Invalid cookies schema.');
}

$c = $this->getBodyBag()->get('cookies', []);

foreach ($cookies as $cookie) {
Expand Down Expand Up @@ -98,4 +117,10 @@ public function forwardCookie(string $name): static
'domain' => $request->getHost(),
]);
}

#[NormalizeGotenbergPayload]
protected function normalizeCookies(): \Generator
{
yield 'cookies' => NormalizerFactory::json(false);
}
}
10 changes: 6 additions & 4 deletions src/Builder/Behaviors/Chromium/CustomHttpHeadersTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
namespace Sensiolabs\GotenbergBundle\Builder\Behaviors\Chromium;

use Sensiolabs\GotenbergBundle\Builder\Attributes\ExposeSemantic;
use Sensiolabs\GotenbergBundle\Builder\Attributes\NormalizeGotenbergPayload;
use Sensiolabs\GotenbergBundle\Builder\BodyBag;
use Sensiolabs\GotenbergBundle\Builder\Util\NormalizerFactory;
use Sensiolabs\GotenbergBundle\Enumeration\NodeType;
use Sensiolabs\GotenbergBundle\PayloadResolver\Util\NormalizerFactory;

trait CustomHttpHeadersTrait
{
Expand All @@ -18,7 +19,7 @@ abstract protected function getBodyBag(): BodyBag;
*
* @see https://gotenberg.dev/docs/routes#custom-http-headers-chromium
*/
#[ExposeSemantic('user_agent', options: ['restrict_to' => 'string'])]
#[ExposeSemantic('user_agent', options: ['default_null' => true, 'restrict_to' => 'string'])]
public function userAgent(string $userAgent): static
{
$this->getBodyBag()->set('userAgent', $userAgent);
Expand All @@ -34,7 +35,7 @@ public function userAgent(string $userAgent): static
*
* @param array<string, string> $headers
*/
#[ExposeSemantic('extra_http_headers', NodeType::Array, ['normalize_keys' => false, 'use_attribute_as_key' => 'name', 'prototype' => 'variable'])]
#[ExposeSemantic('extra_http_headers', NodeType::Array, ['default_value' => [], 'normalize_keys' => false, 'use_attribute_as_key' => 'name', 'prototype' => 'variable'])]
public function extraHttpHeaders(array $headers): static
{
if ([] === $headers) {
Expand Down Expand Up @@ -69,7 +70,8 @@ public function addExtraHttpHeaders(array $headers): static
return $this;
}

protected function normalize(): \Generator
#[NormalizeGotenbergPayload]
protected function normalizeCustomHttpHeader(): \Generator
{
yield 'extraHttpHeaders' => NormalizerFactory::json();
}
Expand Down
Loading

0 comments on commit c870e8e

Please sign in to comment.