8000 Allows to download asset manifest from a remote url · symfony/symfony@3f177be · GitHub
[go: up one dir, main page]

Skip to content

Commit 3f177be

Browse files
committed
Allows to download asset manifest from a remote url
1 parent 7995fed commit 3f177be

File tree

6 files changed

+198
-5
lines changed

6 files changed

+198
-5
lines changed

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -585,6 +585,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode)
585585
->scalarNode('version')->defaultNull()->end()
586586
->scalarNode('version_format')->defaultValue('%%s?%%s')->end()
587587
->scalarNode('json_manifest_path')->defaultNull()->end()
588+
->scalarNode('json_manifest_url')->defaultNull()->end()
588589
->scalarNode('base_path')->defaultValue('')->end()
589590
->arrayNode('base_urls')
590591
->requiresAtLeastOneElement()
@@ -610,6 +611,24 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode)
610611
})
611612
->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets".')
612613
->end()
614+
->validate()
615+
->ifTrue(function ($v) {
616+
return isset($v['version_strategy']) && isset($v['json_manifest_url']);
617+
})
618+
8000 ->thenInvalid('You cannot use both "version_strategy" and "json_manifest_url" at the same time under "assets" packages.')
619+
->end()
620+
->validate()
621+
->ifTrue(function ($v) {
622+
return isset($v['version']) && isset($v['json_manifest_url']);
623+
})
624+
->thenInvalid('You cannot use both "version" and "json_manifest_url" at the same time under "assets" packages.')
625+
->end()
626+
->validate()
627+
->ifTrue(function ($v) {
628+
return isset($v['json_manifest_path']) && isset($v['json_manifest_url']);
629+
})
630+
->thenInvalid('You cannot use both "json_manifest_path" and "json_manifest_url" at the same time under "assets" packages.')
631+
->end()
613632
->fixXmlConfig('package')
614633
->children()
615634
->arrayNode('packages')
@@ -627,6 +646,7 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode)
627646
->end()
628647
->scalarNode('version_format')->defaultNull()->end()
629648
->scalarNode('json_manifest_path')->defaultNull()->end()
649+
->scalarNode('json_manifest_url')->defaultNull()->end()
630650
->scalarNode('base_path')->defaultValue('')->end()
631651
->arrayNode('base_urls')
632652
->requiresAtLeastOneElement()
@@ -652,6 +672,24 @@ private function addAssetsSection(ArrayNodeDefinition $rootNode)
652672
})
653673
->thenInvalid('You cannot use both "version" and "json_manifest_path" at the same time under "assets" packages.')
654674
->end()
675+
->validate()
676+
->ifTrue(function ($v) {
677+
return isset($v['version_strategy']) && isset($v['json_manifest_url']);
678+
})
679+
->thenInvalid('You cannot use both "version_strategy" and "json_manifest_url" at the same time under "assets" packages.')
680+
->end()
681+
->validate()
682+
->ifTrue(function ($v) {
683+
return isset($v['version']) && isset($v['json_manifest_url']);
684+
})
685+
->thenInvalid('You cannot use both "version" and "json_manifest_url" at the same time under "assets" packages.')
686+
->end()
687+
->validate()
688+
->ifTrue(function ($v) {
689+
return isset($v['json_manifest_path']) && isset($v['json_manifest_url']);
690+
})
691+
->thenInvalid('You cannot use both "json_manifest_path" and "json_manifest_url" at the same time under "assets" packages.')
692+
->end()
655693
->end()
656694
->end()
657695
->end()

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -984,7 +984,7 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co
984984
if ($config['version_strategy']) {
985985
$defaultVersion = new Reference($config['version_strategy']);
986986
} else {
987-
$defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], '_default');
987+
$defaultVersion = $this->createVersion($container, $config['version'], $config['version_format'], $config['json_manifest_path'], $config['json_manifest_url'], '_default');
988988
}
989989

990990
$defaultPackage = $this->createPackageDefinition($config['base_path'], $config['base_urls'], $defaultVersion);
@@ -994,14 +994,14 @@ private function registerAssetsConfiguration(array $config, ContainerBuilder $co
994994
foreach ($config['packages'] as $name => $package) {
995995
if (null !== $package['version_strategy']) {
996996
$version = new Reference($package['version_strategy']);
997-
} elseif (!\array_key_exists('version', $package) && null === $package['json_manifest_path']) {
998-
// if neither version nor json_manifest_path are specified, use the default
997+
} elseif (!\array_key_exists('version', $package) && null === $package['json_manifest_path'] && null === $package['json_manifest_url']) {
998+
// if neither version nor json_manifest_path nor json_manifest_url are specified, use the default
999999
$version = $defaultVersion;
10001000
} else {
10011001
// let format fallback to main version_format
10021002
$format = $package['version_format'] ?: $config['version_format'];
10031003
$version = isset($package['version']) ? $package['version'] : null;
1004-
$version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $name);
1004+
$version = $this->createVersion($container, $version, $format, $package['json_manifest_path'], $package['json_manifest_url'], $name);
10051005
}
10061006

10071007
$container->setDefinition('assets._package_'.$name, $this->createPackageDefinition($package['base_path'], $package['base_urls'], $version));
@@ -1034,7 +1034,7 @@ private function createPackageDefinition(?string $basePath, array $baseUrls, Ref
10341034
return $package;
10351035
}
10361036

1037-
private function createVersion(ContainerBuilder $container, ?string $version, ?string $format, ?string $jsonManifestPath, string $name): Reference
1037+
private function createVersion(ContainerBuilder $container, ?string $version, ?string $format, ?string $jsonManifestPath, ?string $jsonManifestUrl, string $name): Reference
10381038
{
10391039
// Configuration prevents $version and $jsonManifestPath from being set
10401040
if (null !== $version) {
@@ -1056,6 +1056,14 @@ private function createVersion(ContainerBuilder $container, ?string $version, ?s
10561056
return new Reference('assets._version_'.$name);
10571057
}
10581058

1059+
if (null !== $jsonManifestUrl) {
1060+
$def = new ChildDefinition('assets.remote_json_manifest_version_strategy');
1061+
$def->replaceArgument(0, $jsonManifestUrl);
1062+
$container->setDefinition('assets._version_'.$name, $def);
1063+
1064+
return new Reference('assets._version_'.$name);
1065+
}
1066+
10591067
return new Reference('assets.empty_version_strategy');
10601068
}
10611069

src/Symfony/Bundle/FrameworkBundle/Resources/config/assets.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,5 +50,10 @@
5050
<service id="assets.json_manifest_version_strategy" class="Symfony\Component\Asset\VersionStrategy\JsonManifestVersionStrategy" abstract="true">
5151
<argument /> <!-- manifest path -->
5252
</service>
53+
54+
<service id="assets.remote_json_manifest_version_strategy" class="Symfony\Component\Asset\VersionStrategy\RemoteJsonManifestVersionStrategy" abstract="true">
55+
<argument /> <!-- manifest url -->
56+
<argument type="service" id="http_client"/>
57+
</service>
5358
</services>
5459
</container>

src/Symfony/Component/Asset/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
5.1.0
5+
-----
6+
7+
* added `RemoteJsonManifestVersionStrategy` to download manifest over HTTP.
8+
49
4.2.0
510
-----
611

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Asset\Tests\VersionStrategy;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Asset\VersionStrategy\RemoteJsonManifestVersionStrategy;
16+
use Symfony\Component\HttpClient\MockHttpClient;
17+
use Symfony\Component\HttpClient\Response\MockResponse;
18+
19+
class RemoteJsonManifestVersionStrategyTest extends TestCase
20+
{
21+
public function testGetVersion()
22+
{
23+
$strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json');
24+
25+
$this->assertEquals('main.123abc.js', $strategy->getVersion('main.js'));
26+
}
27+
28+
public function testApplyVersion()
29+
{
30+
$strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json');
31+
32+
$this->assertEquals('css/styles.555def.css', $strategy->getVersion('css/styles.css'));
33+
}
34+
35+
public function testApplyVersionWhenKeyDoesNotExistInManifest()
36+
{
37+
$strategy = $this->createStrategy('https://cdn.example.com/manifest-valid.json');
38+
39+
$this->assertEquals('css/other.css', $strategy->getVersion('css/other.css'));
40+
}
41+
42+
public function testMissingManifestFileThrowsException()
43+
{
44+
$this->expectException('RuntimeException');
45+
$strategy = $this->createStrategy('https://cdn.example.com/non-existent-file.json');
46+
$strategy->getVersion('main.js');
47+
}
48+
49+
public function testManifestFileWithBadJSONThrowsException()
50+
{
51+
$this->expectException('RuntimeException');
52+
$this->expectExceptionMessage('Error parsing JSON');
53+
$strategy = $this->createStrategy('https://cdn.example.com/manifest-invalid.json');
54+
$strategy->getVersion('main.js');
55+
}
56+
57+
private function createStrategy($manifestUrl)
58+
{
59+
$this->httpClient = new MockHttpClient(function ($method, $url, $options) {
60+
$filename = __DIR__.'/../fixtures/'.basename($url);
61+
62+
if (file_exists($filename)) {
63+
return new MockResponse(file_get_contents($filename));
64+
}
65+
66+
return new MockResponse('{}', ['http_code' => 404]);
67+
});
68+
69+
return new RemoteJsonManifestVersionStrategy($manifestUrl, $this->httpClient);
70+
}
71+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Asset\VersionStrategy;
13+
14+
use Symfony\Contracts\HttpClient\HttpClientInterface;
15+
16+
/**
17+
* Reads the versioned path of an asset from a remote JSON manifest file.
18+
*
19+
* For example, the manifest file might look like this:
20+
* {
21+
* "main.js": "main.abc123.js",
22+
* "css/styles.css": "css/styles.555abc.css"
23+
* }
24+
*
25+
* You could then ask for the version of "main.js" or "css/styles.css".
26+
*/
27+
class RemoteJsonManifestVersionStrategy implements VersionStrategyInterface
28+
{
29+
private $manifestData;
30+
private $httpResponse;
31+
32+
/**
33+
* @param string $manifestUrl Absolute url to the manifest file
34+
*/
35+
public function __construct(string $manifestUrl, HttpClientInterface $httpClient)
36+
{
37+
$this->httpResponse = $httpClient->request('GET', $manifestUrl);
38+
}
39+
40+
/**
41+
* With a manifest, we don't really know or care about what
42+
* the version is. Instead, this returns the path to the
43+
* versioned file.
44+
*/
45+
public function getVersion(string $path)
46+
{
47+
return $this->applyVersion($path);
48+
}
49+
50+
public function applyVersion(string $path)
51+
{
52+
return $this->getManifestPath($path) ?: $path;
53+
}
54+
55+
private function getManifestPath(string $path): ?string
56+
{
57+
if (null === $this->manifestData) {
58+
$this->manifestData = json_decode($this->httpResponse->getContent(), true);
59+
if (0 < json_last_error()) {
60+
throw new \RuntimeException(sprintf('Error parsing JSON from asset manifest file "%s" - %s', $this->httpResponse->getInfo()['url'], json_last_error_msg()));
61+
}
62+
}
63+
64+
return isset($this->manifestData[$path]) ? $this->manifestData[$path] : null;
65+
}
66+
}

0 commit comments

Comments
 (0)
0