Skip to content

Commit d1c6bc0

Browse files
committed
address comments.
1 parent 9662eb1 commit d1c6bc0

1 file changed

Lines changed: 81 additions & 110 deletions

File tree

Lines changed: 81 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
use Utopia\Migration\Transfer;
1717
use Utopia\Storage\Device;
1818

19-
class Csv extends Source
19+
class CSV extends Source
2020
{
2121
private string $filePath;
2222

@@ -25,25 +25,25 @@ class Csv extends Source
2525
*/
2626
private string $resourceId;
2727

28-
private Device $deviceForImports;
28+
private Device $device;
2929

3030
private Reader $database;
3131

3232
public function __construct(
3333
string $resourceId,
3434
string $filePath,
35-
Device $deviceForImports,
35+
Device $device,
3636
?UtopiaDatabase $dbForProject
3737
) {
38+
$this->device = $device;
3839
$this->filePath = $filePath;
3940
$this->resourceId = $resourceId;
40-
$this->deviceForImports = $deviceForImports;
4141
$this->database = new DatabaseReader($dbForProject);
4242
}
4343

4444
public static function getName(): string
4545
{
46-
return 'Csv';
46+
return 'CSV';
4747
}
4848

4949
public static function getSupportedResources(): array
@@ -53,9 +53,26 @@ public static function getSupportedResources(): array
5353
];
5454
}
5555

56+
// called before the `exportGroupDatabases`.
5657
public function report(array $resources = []): array
5758
{
58-
return [];
59+
$report = [];
60+
61+
$this->withCsvStream(function ($stream) use (&$report) {
62+
$headers = fgetcsv($stream);
63+
if (! is_array($headers) || count($headers) === 0) {
64+
return;
65+
}
66+
67+
$rowCount = 0;
68+
while (fgetcsv($stream) !== false) {
69+
$rowCount++;
70+
}
71+
72+
$report[Resource::TYPE_DOCUMENT] = $rowCount;
73+
});
74+
75+
return $report;
5976
}
6077

6178
/**
@@ -84,7 +101,7 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void
84101
);
85102
} finally {
86103
// delete the temporary file!
87-
$this->deviceForImports->delete($this->filePath);
104+
$this->device->delete($this->filePath);
88105
}
89106
}
90107

@@ -93,20 +110,6 @@ protected function exportGroupDatabases(int $batchSize, array $resources): void
93110
*/
94111
private function exportDocuments(int $batchSize): void
95112
{
96-
if (! $this->deviceForImports->exists($this->filePath)) {
97-
return;
98-
}
99-
100-
$stream = fopen($this->filePath, 'r');
101-
if (! $stream) {
102-
return;
103-
}
104-
105-
$headers = fgetcsv($stream);
106-
if (! is_array($headers) || count($headers) === 0) {
107-
fclose($stream);
108-
return;
109-
}
110113

111114
$attributes = [];
112115
$lastAttribute = null;
@@ -141,7 +144,6 @@ private function exportDocuments(int $batchSize): void
141144
$key = $attribute['key'];
142145

143146
if (
144-
// Skip child-side relationships entirely
145147
$attribute['type'] === Attribute::TYPE_RELATIONSHIP &&
146148
($attribute['side'] ?? '') === UtopiaDatabase::RELATION_SIDE_CHILD
147149
) {
@@ -159,113 +161,64 @@ private function exportDocuments(int $batchSize): void
159161
}
160162
}
161163

162-
$buffer = [];
163-
164-
while (($row = fgetcsv($stream)) !== false) {
165-
$data = array_combine($headers, $row);
166-
if ($data === false) {
167-
continue;
164+
$this->withCSVStream(function ($stream) use ($attributeTypes, $manyToManyKeys, $collection, $batchSize) {
165+
$headers = fgetcsv($stream);
166+
if (! is_array($headers) || count($headers) === 0) {
167+
return;
168168
}
169169

170-
$parsedData = $data;
171-
172-
foreach ($data as $key => $value) {
173-
if (! isset($attributeTypes[$key])) {
174-
continue;
175-
}
176-
177-
$type = $attributeTypes[$key];
178-
$parsedValue = trim($value);
170+
$buffer = [];
179171

180-
if ($parsedValue === '') {
172+
while (($row = fgetcsv($stream)) !== false) {
173+
$data = array_combine($headers, $row);
174+
if ($data === false) {
181175
continue;
182176
}
183177

184-
if (in_array($key, $manyToManyKeys, true)) {
185-
$parsedData[$key] = str_contains($parsedValue, ',')
186-
? array_map('trim', explode(',', $parsedValue))
187-
: [$parsedValue];
178+
$parsedData = $data;
188179

189-
continue;
190-
}
180+
foreach ($data as $key => $value) {
181+
$parsedValue = trim($value);
182+
$type = $attributeTypes[$key];
191183

192-
switch ($type) {
193-
case Attribute::TYPE_INTEGER:
194-
$parsedData[$key] = is_numeric($parsedValue) ? (int) $parsedValue : null;
195-
break;
184+
if (! isset($type) || $parsedValue === '') {
185+
continue;
186+
}
196187

197-
case Attribute::TYPE_FLOAT:
198-
$parsedData[$key] = is_numeric($parsedValue) ? (float) $parsedValue : null;
199-
break;
188+
if (in_array($key, $manyToManyKeys, true)) {
189+
$parsedData[$key] = str_contains($parsedValue, ',')
190+
? array_map('trim', explode(',', $parsedValue))
191+
: [$parsedValue];
200192

201-
case Attribute::TYPE_BOOLEAN:
202-
$parsedData[$key] = filter_var($parsedValue, FILTER_VALIDATE_BOOLEAN);
203-
break;
193+
continue;
194+
}
204195

205-
default:
206-
break;
196+
$parsedData[$key] = match ($type) {
197+
Attribute::TYPE_INTEGER => is_numeric($parsedValue) ? (int) $parsedValue : null,
198+
Attribute::TYPE_FLOAT => is_numeric($parsedValue) ? (float) $parsedValue : null,
199+
Attribute::TYPE_BOOLEAN => filter_var($parsedValue, FILTER_VALIDATE_BOOLEAN),
200+
default => $parsedValue,
201+
};
207202
}
208-
}
209203

210-
$permissions = [];
211-
$documentId = $parsedData['$id'] ?? 'unique()';
204+
$documentId = $parsedData['$id'] ?? 'unique()';
212205

213-
if (isset($parsedData['$permissions'])) {
214-
$permissions = $this->parsePermissions($parsedData['$permissions']);
215-
}
206+
// `$id`, `$permissions` in the doc can cause issues!
207+
unset($parsedData['$id'], $parsedData['$permissions']);
216208

217-
unset($parsedData['$id']);
218-
unset($parsedData['$permissions']);
209+
$document = new Document($documentId, $collection, $parsedData);
210+
$buffer[] = $document;
219211

220-
$document = new Document($documentId, $collection, $parsedData, $permissions);
221-
$buffer[] = $document;
212+
if (count($buffer) === $batchSize) {
213+
$this->callback($buffer);
214+
$buffer = [];
215+
}
216+
}
222217

223-
if (count($buffer) === $batchSize) {
218+
if (! empty($buffer)) {
224219
$this->callback($buffer);
225-
$buffer = [];
226220
}
227-
}
228-
229-
fclose($stream);
230-
231-
if (! empty($buffer)) {
232-
$this->callback($buffer);
233-
}
234-
}
235-
236-
/**
237-
* Parses a stringified permission array into a string[].
238-
*
239-
* Example:
240-
* ```
241-
* "[read(\"user:user1234\"),read(\"user:user4321\")]"
242-
* ```
243-
* Into:
244-
* ```
245-
* [
246-
* "read(\"user:user1234\")",
247-
* "read(\"user:user4321\")"
248-
* ]
249-
* ```
250-
*
251-
* @param string $raw
252-
* @return string[]
253-
*/
254-
private function parsePermissions(string $raw): array
255-
{
256-
$raw = trim($raw, ' "[]');
257-
258-
if (empty($raw)) {
259-
return [];
260-
}
261-
262-
$parts = preg_split('/,(?![^(]*\))/', $raw);
263-
264-
return array_map(function ($item) {
265-
$item = trim($item);
266-
267-
return str_replace('\"', '"', $item);
268-
}, $parts);
221+
});
269222
}
270223

271224
/**
@@ -307,4 +260,22 @@ protected function exportGroupFunctions(int $batchSize, array $resources): void
307260
{
308261
throw new \Exception('Not Implemented');
309262
}
263+
264+
private function withCsvStream(callable $fn): void
265+
{
266+
if (! $this->device->exists($this->filePath)) {
267+
return;
268+
}
269+
270+
$stream = fopen($this->filePath, 'r');
271+
if (! $stream) {
272+
return;
273+
}
274+
275+
try {
276+
$fn($stream);
277+
} finally {
278+
fclose($stream);
279+
}
280+
}
310281
}

0 commit comments

Comments
 (0)