Skip to content

Commit 9a87401

Browse files
committed
feat: add backup policy migration support
1 parent 0d6e777 commit 9a87401

6 files changed

Lines changed: 247 additions & 0 deletions

File tree

src/Migration/Destinations/Appwrite.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
use Utopia\Migration\Resources\Functions\Deployment;
4343
use Utopia\Migration\Resources\Functions\EnvVar;
4444
use Utopia\Migration\Resources\Functions\Func;
45+
use Utopia\Migration\Resources\Backups\Policy;
4546
use Utopia\Migration\Resources\Storage\Bucket;
4647
use Utopia\Migration\Resources\Storage\File;
4748
use Utopia\Migration\Transfer;
@@ -128,6 +129,9 @@ public static function getSupportedResources(): array
128129
Resource::TYPE_FUNCTION,
129130
Resource::TYPE_DEPLOYMENT,
130131
Resource::TYPE_ENVIRONMENT_VARIABLE,
132+
133+
// Backups
134+
Resource::TYPE_BACKUP_POLICY,
131135
];
132136
}
133137

@@ -236,6 +240,7 @@ protected function import(array $resources, callable $callback): void
236240
Transfer::GROUP_STORAGE => $this->importFileResource($resource),
237241
Transfer::GROUP_AUTH => $this->importAuthResource($resource),
238242
Transfer::GROUP_FUNCTIONS => $this->importFunctionResource($resource),
243+
Transfer::GROUP_BACKUPS => $this->importBackupResource($resource),
239244
default => throw new \Exception('Invalid resource group'),
240245
};
241246
} catch (\Throwable $e) {
@@ -1443,6 +1448,34 @@ public function importFunctionResource(Resource $resource): Resource
14431448
return $resource;
14441449
}
14451450

1451+
public function importBackupResource(Resource $resource): Resource
1452+
{
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+
];
1462+
1463+
if ($resource->getResourceId()) {
1464+
$params['resourceId'] = $resource->getResourceId();
1465+
$params['resourceType'] = $resource->getResourceType();
1466+
}
1467+
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);
1473+
1474+
$resource->setStatus(Resource::STATUS_SUCCESS);
1475+
1476+
return $resource;
1477+
}
1478+
14461479
/**
14471480
* @throws AppwriteException
14481481
* @throws \Exception

src/Migration/Resource.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ abstract class Resource implements \JsonSerializable
5454

5555
public const TYPE_ENVIRONMENT_VARIABLE = 'environment-variable';
5656

57+
public const TYPE_BACKUP_POLICY = 'backup-policy';
58+
5759
// legacy terminologies
5860
public const TYPE_DOCUMENT = 'document';
5961
public const TYPE_ATTRIBUTE = 'attribute';
@@ -80,6 +82,7 @@ abstract class Resource implements \JsonSerializable
8082
self::TYPE_ENVIRONMENT_VARIABLE,
8183
self::TYPE_TEAM,
8284
self::TYPE_MEMBERSHIP,
85+
self::TYPE_BACKUP_POLICY,
8386

8487
// legacy
8588
self::TYPE_DOCUMENT,
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
<?php
2+
3+
namespace Utopia\Migration\Resources\Backups;
4+
5+
use Utopia\Migration\Resource;
6+
use Utopia\Migration\Transfer;
7+
8+
class Policy extends Resource
9+
{
10+
/**
11+
* @param string $id
12+
* @param string $name
13+
* @param array<string> $services
14+
* @param int $retention
15+
* @param string $schedule
16+
* @param bool $enabled
17+
* @param string $resourceId
18+
* @param string $resourceType
19+
*/
20+
public function __construct(
21+
string $id = '',
22+
private readonly string $name = '',
23+
private readonly array $services = [],
24+
private readonly int $retention = 0,
25+
private readonly string $schedule = '',
26+
private readonly bool $enabled = true,
27+
private readonly string $resourceId = '',
28+
private readonly string $resourceType = '',
29+
) {
30+
$this->id = $id;
31+
}
32+
33+
/**
34+
* @param array<string, mixed> $array
35+
* @return self
36+
*/
37+
public static function fromArray(array $array): self
38+
{
39+
return new self(
40+
$array['id'] ?? '',
41+
$array['name'] ?? '',
42+
$array['services'] ?? [],
43+
$array['retention'] ?? 0,
44+
$array['schedule'] ?? '',
45+
$array['enabled'] ?? true,
46+
$array['resourceId'] ?? '',
47+
$array['resourceType'] ?? '',
48+
);
49+
}
50+
51+
/**
52+
* @return array<string, mixed>
53+
*/
54+
public function jsonSerialize(): array
55+
{
56+
return [
57+
'id' => $this->id,
58+
'name' => $this->name,
59+
'services' => $this->services,
60+
'retention' => $this->retention,
61+
'schedule' => $this->schedule,
62+
'enabled' => $this->enabled,
63+
'resourceId' => $this->resourceId,
64+
'resourceType' => $this->resourceType,
65+
];
66+
}
67+
68+
public static function getName(): string
69+
{
70+
return Resource::TYPE_BACKUP_POLICY;
71+
}
72+
73+
public function getGroup(): string
74+
{
75+
return Transfer::GROUP_BACKUPS;
76+
}
77+
78+
public function getPolicyName(): string
79+
{
80+
return $this->name;
81+
}
82+
83+
/**
84+
* @return array<string>
85+
*/
86+
public function getServices(): array
87+
{
88+
return $this->services;
89+
}
90+
91+
public function getRetention(): int
92+
{
93+
return $this->retention;
94+
}
95+
96+
public function getSchedule(): string
97+
{
98+
return $this->schedule;
99+
}
100+
101+
public function getEnabled(): bool
102+
{
103+
return $this->enabled;
104+
}
105+
106+
public function getResourceId(): string
107+
{
108+
return $this->resourceId;
109+
}
110+
111+
public function getResourceType(): string
112+
{
113+
return $this->resourceType;
114+
}
115+
}

src/Migration/Source.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ public function exportResources(array $resources): void
8989
Transfer::GROUP_DATABASES => Transfer::GROUP_DATABASES_RESOURCES,
9090
Transfer::GROUP_STORAGE => Transfer::GROUP_STORAGE_RESOURCES,
9191
Transfer::GROUP_FUNCTIONS => Transfer::GROUP_FUNCTIONS_RESOURCES,
92+
Transfer::GROUP_BACKUPS => Transfer::GROUP_BACKUPS_RESOURCES,
9293
];
9394

9495
foreach ($mapping as $group => $resources) {
@@ -117,10 +118,18 @@ public function exportResources(array $resources): void
117118
case Transfer::GROUP_FUNCTIONS:
118119
$this->exportGroupFunctions($this->getFunctionsBatchSize(), $resources);
119120
break;
121+
case Transfer::GROUP_BACKUPS:
122+
$this->exportGroupBackups($this->getBackupsBatchSize(), $resources);
123+
break;
120124
}
121125
}
122126
}
123127

128+
public function getBackupsBatchSize(): int
129+
{
130+
return static::$defaultBatchSize;
131+
}
132+
124133
/**
125134
* Export Auth Group
126135
*
@@ -152,4 +161,15 @@ abstract protected function exportGroupStorage(int $batchSize, array $resources)
152161
* @param array<string> $resources Resources to export
153162
*/
154163
abstract protected function exportGroupFunctions(int $batchSize, array $resources): void;
164+
165+
/**
166+
* Export Backups Group
167+
*
168+
* @param int $batchSize
169+
* @param array<string> $resources Resources to export
170+
*/
171+
protected function exportGroupBackups(int $batchSize, array $resources): void
172+
{
173+
// Override in subclasses to support backup policy migration
174+
}
155175
}

src/Migration/Sources/Appwrite.php

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
use Utopia\Migration\Resources\Functions\Deployment;
4040
use Utopia\Migration\Resources\Functions\EnvVar;
4141
use Utopia\Migration\Resources\Functions\Func;
42+
use Utopia\Migration\Resources\Backups\Policy;
4243
use Utopia\Migration\Resources\Storage\Bucket;
4344
use Utopia\Migration\Resources\Storage\File;
4445
use Utopia\Migration\Source;
@@ -143,6 +144,9 @@ public static function getSupportedResources(): array
143144
Resource::TYPE_ENVIRONMENT_VARIABLE,
144145

145146
// Settings
147+
148+
// Backups
149+
Resource::TYPE_BACKUP_POLICY,
146150
];
147151
}
148152

@@ -179,6 +183,7 @@ public function report(array $resources = [], array $resourceIds = []): array
179183
$this->reportDatabases($resources, $report, $resourceIds);
180184
$this->reportStorage($resources, $report, $resourceIds);
181185
$this->reportFunctions($resources, $report, $resourceIds);
186+
$this->reportBackups($resources, $report);
182187

183188
$report['version'] = $this->call(
184189
'GET',
@@ -1614,6 +1619,69 @@ private function exportDeploymentData(Func $func, array $deployment): void
16141619
}
16151620
}
16161621

1622+
protected function exportGroupBackups(int $batchSize, array $resources): void
1623+
{
1624+
try {
1625+
if (\in_array(Resource::TYPE_BACKUP_POLICY, $resources)) {
1626+
$this->exportBackupPolicies($batchSize);
1627+
}
1628+
} catch (\Throwable $e) {
1629+
$this->addError(new Exception(
1630+
Resource::TYPE_BACKUP_POLICY,
1631+
Transfer::GROUP_BACKUPS,
1632+
message: $e->getMessage(),
1633+
code: $e->getCode(),
1634+
previous: $e
1635+
));
1636+
}
1637+
}
1638+
1639+
private function reportBackups(array $resources, array &$report): void
1640+
{
1641+
if (!\in_array(Resource::TYPE_BACKUP_POLICY, $resources)) {
1642+
return;
1643+
}
1644+
1645+
try {
1646+
$response = $this->call('GET', '/backups/policies', [
1647+
'Content-Type' => 'application/json',
1648+
]);
1649+
1650+
$report[Resource::TYPE_BACKUP_POLICY] = $response['total'] ?? 0;
1651+
} catch (\Throwable $e) {
1652+
// Backup policies are Cloud-only, skip gracefully for self-hosted
1653+
$report[Resource::TYPE_BACKUP_POLICY] = 0;
1654+
}
1655+
}
1656+
1657+
private function exportBackupPolicies(int $batchSize): void
1658+
{
1659+
$response = $this->call('GET', '/backups/policies', [
1660+
'Content-Type' => 'application/json',
1661+
]);
1662+
1663+
if (empty($response['policies'])) {
1664+
return;
1665+
}
1666+
1667+
$policies = [];
1668+
1669+
foreach ($response['policies'] as $policy) {
1670+
$policies[] = new Policy(
1671+
$policy['$id'],
1672+
$policy['name'] ?? '',
1673+
$policy['services'] ?? [],
1674+
$policy['retention'] ?? 0,
1675+
$policy['schedule'] ?? '',
1676+
$policy['enabled'] ?? true,
1677+
$policy['resourceId'] ?? '',
1678+
$policy['resourceType'] ?? '',
1679+
);
1680+
}
1681+
1682+
$this->callback($policies);
1683+
}
1684+
16171685
/**
16181686
* Build queries with optional filtering by resource IDs
16191687
*/

src/Migration/Transfer.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class Transfer
1616

1717
public const GROUP_SETTINGS = 'settings';
1818

19+
public const GROUP_BACKUPS = 'backups';
20+
1921
public const GROUP_AUTH_RESOURCES = [
2022
Resource::TYPE_USER,
2123
Resource::TYPE_TEAM,
@@ -44,6 +46,10 @@ class Transfer
4446

4547
public const GROUP_SETTINGS_RESOURCES = [];
4648

49+
public const GROUP_BACKUPS_RESOURCES = [
50+
Resource::TYPE_BACKUP_POLICY,
51+
];
52+
4753
public const ALL_PUBLIC_RESOURCES = [
4854
Resource::TYPE_USER,
4955
Resource::TYPE_TEAM,
@@ -58,6 +64,7 @@ class Transfer
5864
Resource::TYPE_INDEX,
5965
Resource::TYPE_COLUMN,
6066
Resource::TYPE_ROW,
67+
Resource::TYPE_BACKUP_POLICY,
6168

6269
// legacy
6370
Resource::TYPE_DOCUMENT,
@@ -330,6 +337,7 @@ public static function extractServices(array $services): array
330337
self::GROUP_AUTH => array_merge($resources, self::GROUP_AUTH_RESOURCES),
331338
self::GROUP_DATABASES => array_merge($resources, self::GROUP_DATABASES_RESOURCES),
332339
self::GROUP_SETTINGS => array_merge($resources, self::GROUP_SETTINGS_RESOURCES),
340+
self::GROUP_BACKUPS => array_merge($resources, self::GROUP_BACKUPS_RESOURCES),
333341
default => throw new \Exception('No service group found'),
334342
};
335343
}

0 commit comments

Comments
 (0)