Skip to content

Commit

Permalink
stage in array for middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
medilies committed Aug 25, 2024
1 parent 3361373 commit aaa2379
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 10 deletions.
3 changes: 2 additions & 1 deletion src/Commands/RmqStatsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Medilies\RmQ\Facades\RmqException;
use Medilies\RmQ\Models\RmqFile;

class RmqStatsCommand extends Command
Expand Down Expand Up @@ -41,7 +42,7 @@ private function getStatusLabel(int $status): string
RmqFile::STAGED => 'Staged',
RmqFile::DELETED => 'Deleted',
RmqFile::FAILED => 'Failed',
default => 'Unknown',
default => throw new RmqException("Unhandled status '{$status}'"),
};
}
}
14 changes: 11 additions & 3 deletions src/Middleware/RmqMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,26 @@

use Closure;
use Illuminate\Http\Request;
use Medilies\RmQ\Facades\RmQ;
use Illuminate\Support\Facades\Log;
use Medilies\RmQ\RmQ;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

class RmqMiddleware
{
public function __construct(private RmQ $rmq) {}

public function handle(Request $request, Closure $next): Response
{
// ? set singleton to stage in array
$this->rmq->useArray();

$response = $next($request);

RmQ::delete();
try {
$this->rmq->delete();
} catch (Throwable $th) {
Log::error('Failed while deleting the following files ['.implode(', ', $this->rmq->getStore()).'] with error: '.$th->getMessage());
}

return $response;
}
Expand Down
86 changes: 80 additions & 6 deletions src/RmQ.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,58 @@ class RmQ
{
private string $instance;

private bool $useArray = false;

/** @var string[] */
private array $store = [];

public function __construct()
{
$this->instance = (new Ulid)->toRfc4122();
}

public function useArray(): static
{
$this->useArray = true;

return $this;
}

/** @param string[]|string $paths */
public function stage(array|string $paths): void
{
// TODO: take query builder with one selected column
// TODO: take query builder with one selected column => force stageInDb
// ? validate not empty or exists?
// ? Stage in array and persist by the end of the process. The middleware can parametrize the singleton
$this->useArray ?
$this->stageInArray($paths) :
$this->stageInDb($paths);
}

/** @param string[]|string $paths */
private function stageInArray(array|string $paths): void
{
if (is_string($paths)) {
$this->store[] = $paths;

return;
}

/** @var string[] */
$newPaths = collect($paths)
->filter(fn (mixed $path) => is_string($path))
->toArray();

$this->store = array_merge($this->store, $newPaths);
}

/** @param string[]|string $paths */
private function stageInDb(array|string $paths): void
{
$data = match (true) {
is_string($paths) => $this->pathToRecord($paths),
is_array($paths) => collect($paths)
->filter(fn (mixed $path) => is_string($path))
->map(fn (string $path) => $this->pathToRecord($path))
->toArray(),
};
Expand All @@ -36,7 +73,9 @@ public function stage(array|string $paths): void

public function delete(): void
{
$this->performDelete(true);
$this->useArray ?
$this->performDeleteUsingArray() :
$this->performDeleteUsingDb(true);
}

public function deleteAll(): void
Expand All @@ -47,19 +86,20 @@ public function deleteAll(): void
throw new TypeError('rm-q.after must be an integer');
}

$this->performDelete(false, $after);
$this->performDeleteUsingDb(false, $after);
}

/** @return array{path: string, instance: string} */
private function pathToRecord(string $path): array
private function pathToRecord(string $path, int $status = RmqFile::STAGED): array
{
return [
'path' => $path,
'instance' => $this->instance,
'status' => $status,
];
}

private function performDelete(bool $filterInstance = false, int $beforeSeconds = 0): void
private function performDeleteUsingDb(bool $filterInstance = false, int $beforeSeconds = 0): void
{
$now = Date::now();

Expand Down Expand Up @@ -92,4 +132,38 @@ private function performDelete(bool $filterInstance = false, int $beforeSeconds
]);
}
}

private function performDeleteUsingArray(): void
{
$now = Date::now();

$data = [];

foreach ($this->store as $path) {
if (@unlink($path)) {
$data[] = [
'path' => $path,
'instance' => $this->instance,
'status' => RmqFile::DELETED,
'processed_at' => $now,
'deleted_at' => $now,
];
} else {
$data[] = [
'path' => $path,
'instance' => $this->instance,
'status' => RmqFile::FAILED,
'processed_at' => $now,
];
}
}

RmqFile::insert($data);
}

/** @return string[] */
public function getStore(): array
{
return $this->store;
}
}
22 changes: 22 additions & 0 deletions tests/laravel/MiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,27 @@
->assertStatus(200);

$this->assertDatabaseCount(RmqFile::tableName(), $filesCount)
->assertDatabaseHas(RmqFile::tableName(), [
'path' => $files[0],
'status' => RmqFile::DELETED,
])
->assertFileDoesNotExist($files[0]);
});

test('/test-middleware-exception', function () {
/** @var OrchestraTestCase $this */
$files = populateFiles(3);
$filesCount = count($files);

$this->post('/test-middleware-exception', ['files' => $files])
->assertStatus(500);

$this->assertDatabaseCount(RmqFile::tableName(), $filesCount)
->assertDatabaseHas(RmqFile::tableName(), [
'path' => $files[0],
'status' => RmqFile::DELETED,
])
->assertFileDoesNotExist($files[0]);

depopulateFiles($files);
});
13 changes: 13 additions & 0 deletions tests/test-app/TestServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Medilies\RmQ\Facades\RmqException;
use Medilies\RmQ\Middleware\RmqMiddleware;
use Medilies\RmQ\RmQ;

Expand All @@ -21,5 +22,17 @@ public function boot(): void

$rmQ->stage($files);
})->middleware(RmqMiddleware::class);

Route::post('test-middleware-exception', function (Request $request, RmQ $rmQ) {
/** @var array */
$files = $request->validate([
'files' => 'array',
'files.*' => 'string',
])['files'];

$rmQ->stage($files);

throw new RmqException;
})->middleware(RmqMiddleware::class);
}
}

0 comments on commit aaa2379

Please sign in to comment.