|
34 | 34 | use Utopia\Migration\Resources\Auth\Membership; |
35 | 35 | use Utopia\Migration\Resources\Auth\Team; |
36 | 36 | use Utopia\Migration\Resources\Auth\User; |
| 37 | +use Utopia\Migration\Resources\Backups\Policy; |
37 | 38 | use Utopia\Migration\Resources\Database\Column; |
38 | 39 | use Utopia\Migration\Resources\Database\Database; |
39 | 40 | use Utopia\Migration\Resources\Database\Index; |
|
42 | 43 | use Utopia\Migration\Resources\Functions\Deployment; |
43 | 44 | use Utopia\Migration\Resources\Functions\EnvVar; |
44 | 45 | use Utopia\Migration\Resources\Functions\Func; |
45 | | -use Utopia\Migration\Resources\Backups\Policy; |
46 | 46 | use Utopia\Migration\Resources\Storage\Bucket; |
47 | 47 | use Utopia\Migration\Resources\Storage\File; |
48 | 48 | use Utopia\Migration\Transfer; |
@@ -210,6 +210,35 @@ public function report(array $resources = [], array $resourceIds = []): array |
210 | 210 | throw $e; |
211 | 211 | } |
212 | 212 |
|
| 213 | + // Backups (uses call() instead of SDK, so needs separate error handling) |
| 214 | + if (\in_array(Resource::TYPE_BACKUP_POLICY, $resources)) { |
| 215 | + try { |
| 216 | + $scope = 'policies.read'; |
| 217 | + $this->call('GET', '/backups/policies', [ |
| 218 | + 'Content-Type' => 'application/json', |
| 219 | + 'X-Appwrite-Project' => $this->project, |
| 220 | + 'X-Appwrite-Key' => $this->key, |
| 221 | + ]); |
| 222 | + |
| 223 | + $scope = 'policies.write'; |
| 224 | + $this->call('POST', '/backups/policies', [ |
| 225 | + 'Content-Type' => 'application/json', |
| 226 | + 'X-Appwrite-Project' => $this->project, |
| 227 | + 'X-Appwrite-Key' => $this->key, |
| 228 | + ], []); |
| 229 | + } catch (\Throwable $e) { |
| 230 | + $body = \json_decode($e->getMessage(), true); |
| 231 | + if (\is_array($body) && ($body['code'] ?? 0) === 401) { |
| 232 | + $type = $body['type'] ?? ''; |
| 233 | + if ($type === 'additional_resource_not_allowed') { |
| 234 | + throw new \Exception('Backups are not available on the destination project\'s plan', previous: $e); |
| 235 | + } |
| 236 | + throw new \Exception('Missing scope: ' . $scope, previous: $e); |
| 237 | + } |
| 238 | + throw $e; |
| 239 | + } |
| 240 | + } |
| 241 | + |
213 | 242 | return []; |
214 | 243 | } |
215 | 244 |
|
@@ -1448,28 +1477,54 @@ public function importFunctionResource(Resource $resource): Resource |
1448 | 1477 | return $resource; |
1449 | 1478 | } |
1450 | 1479 |
|
| 1480 | + /** |
| 1481 | + * @throws \Exception |
| 1482 | + */ |
1451 | 1483 | public function importBackupResource(Resource $resource): Resource |
1452 | 1484 | { |
1453 | | - /** @var Policy $resource */ |
1454 | | - $params = [ |
1455 | | - 'policyId' => $resource->getId(), |
1456 | | - 'name' => $resource->getPolicyName(), |
1457 | | - 'services' => $resource->getServices(), |
1458 | | - 'enabled' => $resource->getEnabled(), |
1459 | | - 'retention' => $resource->getRetention(), |
1460 | | - 'schedule' => $resource->getSchedule(), |
1461 | | - ]; |
| 1485 | + switch ($resource->getName()) { |
| 1486 | + case Resource::TYPE_BACKUP_POLICY: |
| 1487 | + /** @var Policy $resource */ |
| 1488 | + $params = [ |
| 1489 | + 'policyId' => $resource->getId(), |
| 1490 | + 'name' => $resource->getPolicyName(), |
| 1491 | + 'services' => $resource->getServices(), |
| 1492 | + 'enabled' => $resource->getEnabled(), |
| 1493 | + 'retention' => $resource->getRetention(), |
| 1494 | + 'schedule' => $resource->getSchedule(), |
| 1495 | + ]; |
| 1496 | + |
| 1497 | + if ($resource->getResourceId()) { |
| 1498 | + $collection = match ($resource->getResourceType()) { |
| 1499 | + Resource::TYPE_DATABASE => 'databases', |
| 1500 | + Resource::TYPE_BUCKET => 'buckets', |
| 1501 | + Resource::TYPE_FUNCTION => null, // Functions don't support per-resource backup policies |
| 1502 | + default => null, |
| 1503 | + }; |
| 1504 | + |
| 1505 | + if ($collection !== null) { |
| 1506 | + $doc = $this->database->getDocument($collection, $resource->getResourceId()); |
| 1507 | + if ($doc->isEmpty()) { |
| 1508 | + throw new Exception( |
| 1509 | + resourceName: $resource->getName(), |
| 1510 | + resourceGroup: $resource->getGroup(), |
| 1511 | + resourceId: $resource->getId(), |
| 1512 | + message: 'Referenced ' . $resource->getResourceType() . ' "' . $resource->getResourceId() . '" not found on destination', |
| 1513 | + ); |
| 1514 | + } |
| 1515 | + } |
1462 | 1516 |
|
1463 | | - if ($resource->getResourceId()) { |
1464 | | - $params['resourceId'] = $resource->getResourceId(); |
1465 | | - $params['resourceType'] = $resource->getResourceType(); |
1466 | | - } |
| 1517 | + $params['resourceId'] = $resource->getResourceId(); |
| 1518 | + $params['resourceType'] = $resource->getResourceType(); |
| 1519 | + } |
1467 | 1520 |
|
1468 | | - $this->call('POST', '/backups/policies', [ |
1469 | | - 'Content-Type' => 'application/json', |
1470 | | - 'X-Appwrite-Project' => $this->project, |
1471 | | - 'X-Appwrite-Key' => $this->key, |
1472 | | - ], $params); |
| 1521 | + $this->call('POST', '/backups/policies', [ |
| 1522 | + 'Content-Type' => 'application/json', |
| 1523 | + 'X-Appwrite-Project' => $this->project, |
| 1524 | + 'X-Appwrite-Key' => $this->key, |
| 1525 | + ], $params); |
| 1526 | + break; |
| 1527 | + } |
1473 | 1528 |
|
1474 | 1529 | $resource->setStatus(Resource::STATUS_SUCCESS); |
1475 | 1530 |
|
|
0 commit comments