Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/Assets/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ public function metaPath()

protected function metaExists()
{
return $this->container()->metaFiles()->contains($this->metaPath());
return $this->disk()->exists($this->metaPath());
}

public function writeMeta($meta)
Expand Down
32 changes: 13 additions & 19 deletions src/Assets/AssetContainerContents.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public function all()
return $this->files = $this->cacheStore()->remember($this->key(), $this->ttl(), function () {
return collect($this->getRawFlysystemDirectoryListing())
->keyBy('path')
->reject(fn ($file, $path) => $this->isMetaPath($path))
->map(fn ($file) => $this->normalizeFlysystemAttributes($file))
->pipe(fn ($files) => $this->ensureMissingDirectoriesExist($files))
->sortKeys();
Expand Down Expand Up @@ -182,26 +183,14 @@ public function metaFilesIn($folder, $recursive)
return $this->metaFiles[$key];
}

$files = $this->files();
$path = $recursive ? $folder : trim($folder, '/').'/.meta';

$files = $files->filter(function ($file, $path) {
return Str::startsWith($path, '.meta/')
|| Str::contains($path, '/.meta/');
});

// Filter by folder and recursiveness. But don't bother if we're
// requesting the root recursively as it's already that way.
if ($folder === '/' && $recursive) {
//
} else {
$files = $files->filter(function ($file) use ($folder, $recursive) {
$dir = $file['dirname'];
$dir = substr($dir, 0, -6); // remove .meta/ from the end
$dir = $dir ?: '/';

return $recursive ? Str::startsWith($dir, $folder) : $dir == $folder;
});
}
$files = collect($this->filesystem()->listContents($path, $recursive))
->filter(fn ($attributes) => $attributes->isFile())
->keyBy('path')
->map(fn ($file) => $this->normalizeFlysystemAttributes($file))
->filter(fn ($file, $path) => $this->isMetaPath($path))
->sortKeys();

return $this->metaFiles[$key] = $files;
}
Expand Down Expand Up @@ -268,6 +257,11 @@ private function filesystem()
return $this->container->disk()->filesystem()->getDriver();
}

private function isMetaPath($path): bool
{
return (bool) preg_match('#(^|/)\.meta(/|$)#', $path);
}

public function save()
{
$this->cacheStore()->put($this->key(), $this->all(), $this->ttl());
Expand Down
47 changes: 35 additions & 12 deletions tests/Assets/AssetContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,11 @@ public function it_gets_all_files_by_default()
#[Test]
public function it_gets_all_meta_files_by_default()
{
// The cached listing doesn't include meta files, so they must come from the filesystem.
Cache::put('asset-list-contents-test', collect([
'a.txt' => ['type' => 'file', 'path' => 'a.txt', 'dirname' => ''],
]));

$this->assertEquals([
'.meta/a.txt.yaml',
'.meta/b.txt.yaml',
Expand Down Expand Up @@ -757,12 +762,8 @@ public function it_still_only_gets_the_folders_from_the_cache_and_blink_once_per
#[Test]
public function it_does_not_leak_stale_contents_state_across_calls_when_running_in_a_queue_worker()
{
$cacheKey = 'asset-list-contents-test';

Cache::put($cacheKey, collect([
'a.txt' => ['type' => 'file', 'path' => 'a.txt', 'dirname' => ''],
'.meta/a.txt.yaml' => ['type' => 'file', 'path' => '.meta/a.txt.yaml', 'dirname' => '.meta'],
]));
Storage::fake('test');
Storage::disk('test')->put('.meta/a.txt.yaml', '');

$container = (new AssetContainer)->handle('test')->disk('test');

Expand All @@ -778,12 +779,7 @@ public function it_does_not_leak_stale_contents_state_across_calls_when_running_
);

// Simulate the next job seeing a different state on disk.
Cache::put($cacheKey, collect([
'a.txt' => ['type' => 'file', 'path' => 'a.txt', 'dirname' => ''],
'.meta/a.txt.yaml' => ['type' => 'file', 'path' => '.meta/a.txt.yaml', 'dirname' => '.meta'],
'b.txt' => ['type' => 'file', 'path' => 'b.txt', 'dirname' => ''],
'.meta/b.txt.yaml' => ['type' => 'file', 'path' => '.meta/b.txt.yaml', 'dirname' => '.meta'],
]));
Storage::disk('test')->put('.meta/b.txt.yaml', '');

// Simulate the job boundary: Laravel clears resolved facade instances
// before every job.
Expand All @@ -795,6 +791,33 @@ public function it_does_not_leak_stale_contents_state_across_calls_when_running_
);
}

#[Test]
public function it_does_not_cache_meta_files_or_directories_in_the_listing()
{
$disk = $this->mock(Filesystem::class);
$disk->shouldReceive('filesystem->getDriver->listContents')
->with('/', true)
->once()
->andReturn(new DirectoryListing([
new DirectoryAttributes('.meta'),
new FileAttributes('.meta/one.jpg.yaml'),
new FileAttributes('one.jpg'),
new DirectoryAttributes('one/.meta'),
new FileAttributes('one/.meta/two.jpg.yaml'),
new FileAttributes('one/two.jpg'),
]));

File::shouldReceive('disk')->with('test')->andReturn($disk);

$container = (new AssetContainer)->handle('test')->disk('test');

$this->assertEquals(['one.jpg', 'one/two.jpg'], $container->files()->all());
$this->assertEquals(
['one', 'one.jpg', 'one/two.jpg'],
Cache::get('asset-list-contents-test')->keys()->all()
);
}

#[Test]
public function it_gets_an_asset()
{
Expand Down
16 changes: 16 additions & 0 deletions tests/Assets/AssetTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2904,6 +2904,22 @@ public function it_resolves_pending_original_meta_values_when_hydrating()
], $asset->getRawOriginal());
}

#[Test]
public function it_resolves_pending_original_meta_values_from_disk_when_the_listing_is_loaded()
{
Storage::disk('test')->put('path/to/test.txt', '');

// Warm the container listing before the meta file exists.
$this->container->files();

Storage::disk('test')->put('path/to/.meta/test.txt.yaml', "data:\n alfa: bravo");

$asset = (new Asset)->container($this->container)->path('path/to/test.txt');
$asset->syncOriginal();

$this->assertEquals(['alfa' => 'bravo'], $asset->getOriginal('data'));
}

#[Test]
#[DataProvider('warmPresetProvider')]
public function it_gets_which_presets_to_warm($extension, $orientation, $cpEnabled, $expectedWarm)
Expand Down
Loading