Skip to content
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
3 changes: 3 additions & 0 deletions composer-dependency-analyser.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
->ignoreErrorsOnPackage('twig/twig', [
ErrorType::DEV_DEPENDENCY_IN_PROD,
])
->ignoreErrorsOnPackage('symfony/asset', [
ErrorType::DEV_DEPENDENCY_IN_PROD,
])
;

if (\PHP_VERSION_ID < 80200) { // TODO: Requires PHP >= 8.2
Expand Down
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"require": {
"php": ">=8.1",
"ext-json": "*",
"ext-filter": "*",
"psr/container": "^2.0",
"psr/log": "^3.0",
"symfony/config": "^6.4 || ^7.0 || ^8.0",
Expand All @@ -45,6 +46,7 @@
"phpstan/phpstan-symfony": "^2.0",
"phpunit/phpunit": "^10.5.46",
"shipmonk/composer-dependency-analyser": "^1.8",
"symfony/asset": "^6.4 || ^7.0 || ^8.0",
"symfony/error-handler": "^6.4 || ^7.0 || ^8.0",
"symfony/framework-bundle": "^6.4 || ^7.0 || ^8.0",
"symfony/http-client": "^6.4 || ^7.0 || ^8.0",
Expand Down
1 change: 1 addition & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
->tag('twig.extension')
;
$services->set('sensiolabs_gotenberg.twig.asset_runtime', GotenbergRuntime::class)
->args([service('assets.packages')->nullOnInvalid()])
->tag('twig.runtime')
;

Expand Down
7 changes: 5 additions & 2 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ public function getConfigTreeBuilder(): TreeBuilder
$treeBuilder->getRootNode()
->addDefaultsIfNotSet()
->children()
->scalarNode('assets_directory')
->arrayNode('assets_directory')
->normalizeKeys(false)
->prototype('scalar')->end()
->info('Base directory will be used for assets, files, markdown')
->defaultValue('%kernel.project_dir%/assets')
->defaultValue(['%kernel.project_dir%/assets'])
->beforeNormalization()->castToArray()->end()
->end()
->scalarNode('version')
->info('Version of Gotenberg')
Expand Down
37 changes: 29 additions & 8 deletions src/Formatter/AssetBaseDirFormatter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,53 @@

namespace Sensiolabs\GotenbergBundle\Formatter;

use Symfony\Component\Filesystem\Exception\FileNotFoundException;
use Symfony\Component\Filesystem\Path;

/**
* @internal
*/
final class AssetBaseDirFormatter
{
private readonly string $baseDir;
/** @var string[] */
private readonly array $baseDir;

/** @var array<string, string> */
private array $resolvedPathsCache = [];

/**
* @param string[] $baseDir
*/
public function __construct(
private readonly string $projectDir,
string $baseDir,
array $baseDir,
) {
$this->baseDir = rtrim($baseDir, '/\\');
$this->baseDir = array_map(static fn ($value) => rtrim($value, '/\\'), $baseDir);
}

public function resolve(string $path): string
{
if (Path::isAbsolute($path)) {
return $path;
if (\array_key_exists($path, $this->resolvedPathsCache)) {
return $this->resolvedPathsCache[$path];
}

if (Path::isAbsolute($path) || filter_var($path, \FILTER_VALIDATE_URL)) {
return $this->resolvedPathsCache[$path] = $path;
}

if (Path::isAbsolute($this->baseDir)) {
return Path::join($this->baseDir, $path);
foreach ($this->baseDir as $baseDir) {
if (!Path::isAbsolute($baseDir)) {
$baseDir = Path::join($this->projectDir, $baseDir);
}

$filename = Path::join($baseDir, $path);
if (!file_exists($filename)) {
continue;
}

return $this->resolvedPathsCache[$path] = $filename;
}

return Path::join($this->projectDir, $this->baseDir, $path);
throw new FileNotFoundException(\sprintf('File "%s" not found in assets directories: "%s".', $path, implode('", "', $this->baseDir)));
}
}
2 changes: 1 addition & 1 deletion src/Test/Builder/GotenbergBuilderTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected function setUp(): void
$this->client = new GotenbergClientAsserter();
$this->container = new Container();

$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(static::FIXTURE_DIR, static::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(static::FIXTURE_DIR, [static::FIXTURE_DIR]));
$this->container->set('sensiolabs_gotenberg.client', $this->client);
$this->container->set('sensiolabs_gotenberg.version_fetcher', new StaticVersionFetcher($this->gotenbergVersion));
}
Expand Down
18 changes: 18 additions & 0 deletions src/Twig/GotenbergRuntime.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Sensiolabs\GotenbergBundle\Twig;

use Sensiolabs\GotenbergBundle\Builder\BuilderAssetInterface;
use Symfony\Component\Asset\Packages;

/**
* @internal
Expand All @@ -14,6 +15,10 @@ final class GotenbergRuntime
{
private BuilderAssetInterface|null $builder = null;

public function __construct(private readonly Packages|null $packages = null)
{
}

public function setBuilder(BuilderAssetInterface|null $builder): void
{
$this->builder = $builder;
Expand All @@ -27,20 +32,23 @@ public function setBuilder(BuilderAssetInterface|null $builder): void
*/
public function getAssetUrl(string $path): string
{
$path = $this->getVersionedPathIfExist($path);
$this->addAsset($path, 'gotenberg_asset');

return basename($path);
}

public function getFontStyleTag(string $path, string $name): string
{
$path = $this->getVersionedPathIfExist($path);
$this->addAsset($path, 'gotenberg_font_style_tag');

return '<style>'.$this->generateFontFace($path, $name).'</style>';
}

public function getFontFace(string $path, string $name): string
{
$path = $this->getVersionedPathIfExist($path);
$this->addAsset($path, 'gotenberg_font_face');

return $this->generateFontFace($path, $name);
Expand All @@ -62,4 +70,14 @@ private function addAsset(string $path, string $function): void

$this->builder->addAsset($path);
}

private function getVersionedPathIfExist(string $path): string
{
$packages = $this->packages;
if (null !== $packages) {
$path = ltrim($packages->getUrl($path), '/');
}

return $path;
}
}
2 changes: 1 addition & 1 deletion tests/Builder/Pdf/ConvertPdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public function testFilesExtensionRequirement(): void
$this->expectExceptionMessage('The file extension "png" is not valid in this context.');

$this->getBuilder()
->files('b.png')
->files(self::FIXTURE_DIR.'/assets/logo.png')
->generate()
;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Builder/Pdf/FlattenPdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public function testFilesExtensionRequirement(): void
$this->expectExceptionMessage('The file extension "png" is not valid in this context.');

$this->getBuilder()
->files('b.png')
->files(self::FIXTURE_DIR.'/assets/logo.png')
->generate()
;
}
Expand Down
12 changes: 6 additions & 6 deletions tests/Builder/Pdf/HtmlPdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public function testRequiredFormData(): void

public function testOutputFilename(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$this->getBuilder()
->contentFile('files/content.html')
Expand All @@ -64,7 +64,7 @@ public function testOutputFilename(): void

public function testWidth(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$this->getBuilder()
->contentFile('files/content.html')
Expand All @@ -83,7 +83,7 @@ public function testWidth(): void

public function testWithTwigContentFile(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$twig = new Environment(new FilesystemLoader(self::FIXTURE_DIR), [
'strict_variables' => true,
Expand Down Expand Up @@ -123,7 +123,7 @@ public function load(string $class): object|null

public function testWithTwigAndHeaderFooterParts(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$twig = new Environment(new FilesystemLoader(self::FIXTURE_DIR), [
'strict_variables' => true,
Expand Down Expand Up @@ -195,7 +195,7 @@ public function load(string $class): object|null

public function testFilesAsHeaderAndFooter(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$this->getBuilder()
->headerFile('files/header.html')
Expand All @@ -217,7 +217,7 @@ public function testWithInvalidTwigTemplate(): void
{
$this->expectException(PartRenderingException::class);

$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$twig = new Environment(new FilesystemLoader(self::FIXTURE_DIR), [
'strict_variables' => true,
Expand Down
2 changes: 1 addition & 1 deletion tests/Builder/Pdf/MarkdownPdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public function testFilesExtensionRequirement(): void
$this->expectExceptionMessage('The file extension "png" is not valid in this context.');

$this->getBuilder()
->files('b.png')
->files(self::FIXTURE_DIR.'/assets/logo.png')
->generate()
;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Builder/Pdf/MergePdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function testFilesExtensionRequirement(): void
$this->expectExceptionMessage('The file extension "png" is not valid in this context.');

$this->getBuilder()
->files('simple_pdf.pdf', 'b.png')
->files(self::FIXTURE_DIR.'/assets/logo.png')
->generate()
;
}
Expand Down
2 changes: 1 addition & 1 deletion tests/Builder/Pdf/SplitPdfBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public function testFilesExtensionRequirement(): void
$this->expectExceptionMessage('The file extension "png" is not valid in this context.');

$this->getBuilder()
->files('b.png')
->files(self::FIXTURE_DIR.'/assets/logo.png')
->splitMode(SplitMode::Pages)
->splitSpan('1-2')
->generate()
Expand Down
2 changes: 1 addition & 1 deletion tests/Builder/Screenshot/HtmlScreenshotBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected function initializeBuilder(BuilderInterface $builder, Container $conta

public function testOutputFilename(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$this->getBuilder()
->contentFile('files/content.html')
Expand Down
4 changes: 2 additions & 2 deletions tests/Builder/Screenshot/MarkdownScreenshotBuilderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ protected function initializeBuilder(BuilderInterface $builder, Container $conta

public function testOutputFilename(): void
{
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, self::FIXTURE_DIR));
$this->container->set('asset_base_dir_formatter', new AssetBaseDirFormatter(self::FIXTURE_DIR, [self::FIXTURE_DIR]));

$this->getBuilder()
->wrapperFile('files/content.html')
Expand Down Expand Up @@ -137,7 +137,7 @@ public function testFilesExtensionRequirement(): void
$this->expectExceptionMessage('The file extension "png" is not valid in this context.');

$this->getBuilder()
->files('b.png')
->files(self::FIXTURE_DIR.'/assets/logo.png')
->generate()
;
}
Expand Down
4 changes: 2 additions & 2 deletions tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ public function testInvalidWebhookConfiguration(array $config): void

/**
* @return array{
* 'assets_directory': string,
* 'assets_directory': string[],
* 'http_client': string,
* 'webhook': array<void>,
* 'default_options': array{
Expand All @@ -235,7 +235,7 @@ private static function getBundleDefaultConfig(): array
{
return [
'version' => null,
'assets_directory' => '%kernel.project_dir%/assets',
'assets_directory' => ['%kernel.project_dir%/assets'],
'http_client' => 'http_client',
'webhook' => [],
'controller_listener' => true,
Expand Down
29 changes: 21 additions & 8 deletions tests/Formatter/AssetBaseDirFormatterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,23 +9,36 @@

final class AssetBaseDirFormatterTest extends TestCase
{
private const PROJECT_DIR = __DIR__.'/../';

/**
* @return iterable<string, array<int, string>>
* @return iterable<string, array<int, list<string>|string>>
*/
public static function generateBaseDirectoryAndPath(): iterable
{
yield 'absolute path and absolute base dir' => ['/mock/foo/file.md', '/mock/bar', '/mock/foo/file.md'];
yield 'absolute path and relative base dir' => ['/mock/foo/logo.png', '/bar', '/mock/foo/logo.png'];
yield 'relative path and relative base dir' => ['document.odt', 'bar/baz', '/mock/bar/baz/document.odt'];
yield 'relative path and absolute base dir' => ['foo/document.odt', '/mock/bar/baz', '/mock/bar/baz/foo/document.odt'];
yield 'relative path and relative base dir with end slash' => ['document.odt', 'bar/baz/', '/mock/bar/baz/document.odt'];
$projectDir = \dirname(self::PROJECT_DIR, 2);

yield 'absolute path and absolute base dir' => [$projectDir.'/Fixtures/assets/file.md', [$projectDir.'/Fixtures/assets'], $projectDir.'/Fixtures/assets/file.md'];
yield 'absolute path and relative base dir' => [$projectDir.'/Fixtures/assets/file.md', ['assets'], $projectDir.'/Fixtures/assets/file.md'];
yield 'relative path and relative base dir' => ['file.md', ['Fixtures/assets'], $projectDir.'/Fixtures/assets/file.md'];
yield 'relative path and absolute base dir' => ['office/document.odt', [$projectDir.'/Fixtures/assets'], $projectDir.'/Fixtures/assets/office/document.odt'];
yield 'relative path and relative base dir with end slash' => ['document.odt', ['Fixtures/assets/office/'], $projectDir.'/Fixtures/assets/office/document.odt'];
yield 'URL path and absolute base dir' => ['https://sensiolabs.com/assets/images/sensiolabs/sensiolabs.fr-OAnPSf0.png', [$projectDir.'/Fixtures/assets'], 'https://sensiolabs.com/assets/images/sensiolabs/sensiolabs.fr-OAnPSf0.png'];
yield 'URL path and relative base dir' => ['https://sensiolabs.com/assets/images/sensiolabs/sensiolabs.fr-OAnPSf0.png', ['assets'], 'https://sensiolabs.com/assets/images/sensiolabs/sensiolabs.fr-OAnPSf0.png'];
yield 'absolute path and two absolute base dir' => [$projectDir.'/Fixtures/assets/office/document.odt', [$projectDir.'/Fixtures/assets', $projectDir.'/Fixtures/assets/office'], $projectDir.'/Fixtures/assets/office/document.odt'];
yield 'absolute path and two relative base dir' => [$projectDir.'/Fixtures/assets/office/document.odt', ['Fixtures/assets', 'Fixtures/assets/office'], $projectDir.'/Fixtures/assets/office/document.odt'];
yield 'relative path and two absolute base dir' => ['document.odt', [$projectDir.'/Fixtures/assets', $projectDir.'/Fixtures/assets/office'], $projectDir.'/Fixtures/assets/office/document.odt'];
yield 'relative path and two relative base dir' => ['document.odt', ['Fixtures/assets', 'Fixtures/assets/office'], $projectDir.'/Fixtures/assets/office/document.odt'];
}

/**
* @param string[] $baseDirectories
*/
#[DataProvider('generateBaseDirectoryAndPath')]
#[TestDox('Resolve path when "$_dataName"')]
public function testResolvePathCorrectly(string $path, string $baseDirectory, string $expectedResult): void
public function testResolvePathCorrectly(string $path, array $baseDirectories, string $expectedResult): void
{
$assetBaseDirFormatter = new AssetBaseDirFormatter('/mock', $baseDirectory);
$assetBaseDirFormatter = new AssetBaseDirFormatter(self::PROJECT_DIR, $baseDirectories);
$resolvedPath = $assetBaseDirFormatter->resolve($path);
self::assertSame($expectedResult, $resolvedPath);
}
Expand Down