• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LeTraceurSnorkLibrary / MessSaga / 23844646769

01 Apr 2026 10:42AM UTC coverage: 31.674% (+31.1%) from 0.549%
23844646769

push

github

web-flow
feat: media files import

431 of 801 new or added lines in 30 files covered. (53.81%)

4 existing lines in 4 files now uncovered.

439 of 1386 relevant lines covered (31.67%)

0.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/app/Jobs/ProcessChatImport.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace App\Jobs;
6

7
use App\Services\Import\Archives\DTO\ArchiveExtractionResult;
8
use App\Services\Import\Archives\Exceptions\ArchiveExtractionFailedException;
9
use App\Services\Import\Factories\ImportArchiveExtractorFactory;
10
use App\Services\Import\Strategies\ImportOnlyMediaFilesStrategyInterface;
11
use App\Services\Import\Strategies\ImportStrategyInterface;
12
use App\Services\ImportService;
13
use App\Services\Media\MediaFileStorageService;
14
use App\Services\Parsers\ParserRegistry;
15
use Illuminate\Bus\Queueable;
16
use Illuminate\Contracts\Queue\ShouldQueue;
17
use Illuminate\Database\QueryException;
18
use Illuminate\Foundation\Bus\Dispatchable;
19
use Illuminate\Queue\InteractsWithQueue;
20
use Illuminate\Queue\SerializesModels;
21
use Illuminate\Support\Facades\Log;
22
use Illuminate\Support\Facades\Storage;
23

24
class ProcessChatImport implements ShouldQueue
25
{
26
    use Dispatchable;
27
    use InteractsWithQueue;
28
    use Queueable;
29
    use SerializesModels;
30

31
    /**
32
     * @param int                     $userId
33
     * @param string                  $messengerType
34
     * @param string                  $exportFileStoredPath Storage-relative path (archive or single export file)
35
     * @param ImportStrategyInterface $strategy
36
     */
37
    public function __construct(
38
        public int                     $userId,
39
        public string                  $messengerType,
40
        public string                  $exportFileStoredPath,
41
        public ImportStrategyInterface $strategy
42
    ) {
43
    }
×
44

45
    /**
46
     * @param ImportArchiveExtractorFactory $archiveExtractorsFactory
47
     * @param ParserRegistry                $parserRegistry
48
     * @param MediaFileStorageService       $mediaFileStorageService
49
     *
50
     * @return void
51
     */
52
    public function handle(
53
        ImportArchiveExtractorFactory $archiveExtractorsFactory,
54
        ParserRegistry                $parserRegistry,
55
        MediaFileStorageService       $mediaFileStorageService
56
    ): void {
NEW
57
        $source             = null;
×
NEW
58
        $extractedDir       = null;
×
NEW
59
        $shouldDeleteSource = true;
×
60

61
        try {
NEW
62
            $archiveExtractor = $archiveExtractorsFactory->makeForPath($this->exportFileStoredPath);
×
NEW
63
            if ($archiveExtractor !== null) {
×
NEW
64
                $source = $archiveExtractor->extract($this->exportFileStoredPath, $this->messengerType);
×
65

NEW
66
                $extractedDir = $source->getExtractedDir();
×
67

68
                /**
69
                 * If export file (e.g. result.json) is not presented - we cannot perform import
70
                 */
NEW
71
                if ($source->getExportFileAbsolutePath() === null) {
×
72
                    /**
73
                     * Though, if import mode is 'To selected conversation' - we can try to import media to already
74
                     * existing conversation
75
                     */
NEW
76
                    $this->runMediaOnlyFallback(
×
NEW
77
                        archiveExtractorsFactory: $archiveExtractorsFactory,
×
NEW
78
                        parserRegistry: $parserRegistry,
×
NEW
79
                        mediaFileStorageService: $mediaFileStorageService
×
NEW
80
                    );
×
81

NEW
82
                    return;
×
83
                }
84
            }
NEW
85
            $extractedExportFile = $source ?? new ArchiveExtractionResult(
×
NEW
86
                Storage::path($this->exportFileStoredPath),
×
NEW
87
                null,
×
NEW
88
                null
×
NEW
89
            );
×
90

91
            /**
92
             * @var ImportService $service
93
             */
94
            $service = app(ImportService::class);
×
95

96
            /**
97
             * Импорт читает файл экспорта из $extractedExportFile и при наличии медиа копирует их из распакованной
98
             * папки в постоянное хранилище (Storage).
99
             * К моменту выхода из import() файлы уже лежат в conversations/{id}/media/.
100
             */
101
            $service->import(
×
102
                userId: $this->userId,
×
103
                messengerType: $this->messengerType,
×
NEW
104
                strategy: $this->strategy,
×
NEW
105
                extractedExportFile: $extractedExportFile
×
UNCOV
106
            );
×
NEW
107
        } catch (ArchiveExtractionFailedException $e) {
×
NEW
108
            Log::warning('Archive extraction failed', [
×
NEW
109
                'user_id'          => $this->userId,
×
NEW
110
                'messenger_type'   => $this->messengerType,
×
NEW
111
                'export_file_path' => $this->exportFileStoredPath,
×
NEW
112
                'reason'           => $e->getMessage(),
×
NEW
113
            ]);
×
NEW
114
        } catch (QueryException $e) {
×
NEW
115
            Log::error('Import failed due to database query error', [
×
NEW
116
                'user_id'          => $this->userId,
×
NEW
117
                'messenger_type'   => $this->messengerType,
×
NEW
118
                'export_file_path' => $this->exportFileStoredPath,
×
NEW
119
                'strategy'         => $this->strategy->getName(),
×
NEW
120
                'error'            => $e->getMessage(),
×
NEW
121
            ]);
×
122

NEW
123
            $shouldDeleteSource = false;
×
NEW
124
            throw $e;
×
125
        } finally {
126
            /**
127
             * Delete temporary archive/export file
128
             */
NEW
129
            if ($shouldDeleteSource && Storage::exists($this->exportFileStoredPath)) {
×
NEW
130
                Storage::delete($this->exportFileStoredPath);
×
131
            }
132

133
            /**
134
             * Delete extraction directory
135
             */
NEW
136
            if (isset($extractedDir) && Storage::exists($extractedDir)) {
×
NEW
137
                Storage::deleteDirectory($extractedDir);
×
138
            }
139
        }
140
    }
141

142
    /**
143
     * Fallback for "Import to selected conversation" option when archive has additional media files that should be
144
     * merged into selected conversation
145
     *
146
     * @param ImportArchiveExtractorFactory $archiveExtractorsFactory
147
     * @param ParserRegistry                $parserRegistry
148
     * @param MediaFileStorageService       $mediaFileStorageService
149
     *
150
     * @return void
151
     */
152
    private function runMediaOnlyFallback(
153
        ImportArchiveExtractorFactory $archiveExtractorsFactory,
154
        ParserRegistry                $parserRegistry,
155
        MediaFileStorageService       $mediaFileStorageService
156
    ): void {
157
        /**
158
         * This fallback is only for "Import to selected conversation" scenario
159
         */
NEW
160
        if (!$this->strategy instanceof ImportOnlyMediaFilesStrategyInterface) {
×
NEW
161
            return;
×
162
        }
163

NEW
164
        $targetConversationId = $this->strategy->getImportMode()?->getTargetConversationId();
×
NEW
165
        if (!isset($targetConversationId)) {
×
NEW
166
            return;
×
167
        }
168

NEW
169
        $mediaUploadJob = new ProcessConversationMediaUpload(
×
NEW
170
            userId: $this->userId,
×
NEW
171
            conversationId: $targetConversationId,
×
NEW
172
            path: $this->exportFileStoredPath
×
NEW
173
        );
×
174

NEW
175
        $mediaUploadJob->handle(
×
NEW
176
            parserRegistry: $parserRegistry,
×
NEW
177
            mediaFileStorageService: $mediaFileStorageService,
×
NEW
178
            archiveExtractorsFactory: $archiveExtractorsFactory
×
NEW
179
        );
×
180
    }
181
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc