Skip to content

Commit 0d10f71

Browse files
authored
Merge pull request #184 from utopia-php/add-project-variable-migration
Add project variable migration support
2 parents 79d9a10 + 6443bd0 commit 0d10f71

11 files changed

Lines changed: 288 additions & 2 deletions

File tree

src/Migration/Destinations/Appwrite.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
use Utopia\Migration\Resources\Messaging\Provider;
5757
use Utopia\Migration\Resources\Messaging\Subscriber;
5858
use Utopia\Migration\Resources\Messaging\Topic;
59+
use Utopia\Migration\Resources\Settings\ProjectVariable;
5960
use Utopia\Migration\Resources\Sites\Deployment as SiteDeployment;
6061
use Utopia\Migration\Resources\Sites\EnvVar as SiteEnvVar;
6162
use Utopia\Migration\Resources\Sites\Site;
@@ -278,6 +279,9 @@ public static function getSupportedResources(): array
278279
Resource::TYPE_PLATFORM,
279280
Resource::TYPE_API_KEY,
280281

282+
// Settings
283+
Resource::TYPE_PROJECT_VARIABLE,
284+
281285
// Backups
282286
Resource::TYPE_BACKUP_POLICY,
283287
];
@@ -438,6 +442,7 @@ protected function import(array $resources, callable $callback): void
438442
Transfer::GROUP_SITES => $this->importSiteResource($resource),
439443
Transfer::GROUP_INTEGRATIONS => $this->importIntegrationsResource($resource),
440444
Transfer::GROUP_BACKUPS => $this->importBackupResource($resource),
445+
Transfer::GROUP_SETTINGS => $this->importSettingsResource($resource),
441446
default => throw new \Exception('Invalid resource group', Exception::CODE_VALIDATION),
442447
};
443448
} catch (\Throwable $e) {
@@ -3089,6 +3094,61 @@ public function importIntegrationsResource(Resource $resource): Resource
30893094
return $resource;
30903095
}
30913096

3097+
public function importSettingsResource(Resource $resource): Resource
3098+
{
3099+
switch ($resource->getName()) {
3100+
case Resource::TYPE_PROJECT_VARIABLE:
3101+
/** @var ProjectVariable $resource */
3102+
$this->createProjectVariable($resource);
3103+
break;
3104+
}
3105+
3106+
if ($resource->getStatus() !== Resource::STATUS_SKIPPED) {
3107+
$resource->setStatus(Resource::STATUS_SUCCESS);
3108+
}
3109+
3110+
return $resource;
3111+
}
3112+
3113+
protected function createProjectVariable(ProjectVariable $resource): bool
3114+
{
3115+
$existing = $this->dbForProject->findOne('variables', [
3116+
Query::equal('resourceType', ['project']),
3117+
Query::equal('key', [$resource->getKey()]),
3118+
]);
3119+
3120+
if ($existing !== false && !$existing->isEmpty()) {
3121+
$resource->setStatus(Resource::STATUS_SKIPPED, 'Project variable already exists');
3122+
return false;
3123+
}
3124+
3125+
$createdAt = $this->normalizeDateTime($resource->getCreatedAt());
3126+
$updatedAt = $this->normalizeDateTime($resource->getUpdatedAt(), $createdAt);
3127+
$variableId = ID::unique();
3128+
$key = $resource->getKey();
3129+
3130+
try {
3131+
$this->dbForProject->createDocument('variables', new UtopiaDocument([
3132+
'$id' => $variableId,
3133+
'$permissions' => $resource->getPermissions(),
3134+
'resourceInternalId' => '',
3135+
'resourceId' => '',
3136+
'resourceType' => 'project',
3137+
'key' => $key,
3138+
'value' => $resource->getValue(),
3139+
'secret' => $resource->isSecret(),
3140+
'search' => \implode(' ', [$variableId, $key, 'project']),
3141+
'$createdAt' => $createdAt,
3142+
'$updatedAt' => $updatedAt,
3143+
]));
3144+
} catch (DuplicateException) {
3145+
$resource->setStatus(Resource::STATUS_SKIPPED, 'Project variable already exists');
3146+
return false;
3147+
}
3148+
3149+
return true;
3150+
}
3151+
30923152
/**
30933153
* @throws \Throwable
30943154
*/

src/Migration/Resource.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,12 @@ abstract class Resource implements \JsonSerializable
7474
// Integrations
7575
public const TYPE_PLATFORM = 'platform';
7676
public const TYPE_API_KEY = 'api-key';
77-
public const TYPE_SUBSCRIBER = 'subscriber';
7877

78+
// Settings
79+
public const TYPE_PROJECT_VARIABLE = 'project-variable';
80+
81+
// Messaging
82+
public const TYPE_SUBSCRIBER = 'subscriber';
7983
public const TYPE_MESSAGE = 'message';
8084

8185
// Backups
@@ -114,6 +118,7 @@ abstract class Resource implements \JsonSerializable
114118
self::TYPE_MEMBERSHIP,
115119
self::TYPE_PLATFORM,
116120
self::TYPE_API_KEY,
121+
self::TYPE_PROJECT_VARIABLE,
117122
self::TYPE_PROVIDER,
118123
self::TYPE_TOPIC,
119124
self::TYPE_SUBSCRIBER,
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
namespace Utopia\Migration\Resources\Settings;
4+
5+
use Utopia\Migration\Resource;
6+
use Utopia\Migration\Transfer;
7+
8+
class ProjectVariable extends Resource
9+
{
10+
public function __construct(
11+
string $id,
12+
private readonly string $key,
13+
private readonly string $value = '',
14+
private readonly bool $secret = false,
15+
string $createdAt = '',
16+
string $updatedAt = '',
17+
) {
18+
$this->id = $id;
19+
$this->createdAt = $createdAt;
20+
$this->updatedAt = $updatedAt;
21+
}
22+
23+
/**
24+
* @param array<string, mixed> $array
25+
* @return self
26+
*/
27+
public static function fromArray(array $array): self
28+
{
29+
return new self(
30+
$array['id'],
31+
$array['key'],
32+
$array['value'] ?? '',
33+
(bool) ($array['secret'] ?? false),
34+
createdAt: $array['createdAt'] ?? '',
35+
updatedAt: $array['updatedAt'] ?? '',
36+
);
37+
}
38+
39+
/**
40+
* @return array<string, mixed>
41+
*/
42+
public function jsonSerialize(): array
43+
{
44+
return [
45+
'id' => $this->id,
46+
'key' => $this->key,
47+
'value' => $this->value,
48+
'secret' => $this->secret,
49+
'createdAt' => $this->createdAt,
50+
'updatedAt' => $this->updatedAt,
51+
];
52+
}
53+
54+
public static function getName(): string
55+
{
56+
return Resource::TYPE_PROJECT_VARIABLE;
57+
}
58+
59+
public function getGroup(): string
60+
{
61+
return Transfer::GROUP_SETTINGS;
62+
}
63+
64+
public function getKey(): string
65+
{
66+
return $this->key;
67+
}
68+
69+
public function getValue(): string
70+
{
71+
return $this->value;
72+
}
73+
74+
public function isSecret(): bool
75+
{
76+
return $this->secret;
77+
}
78+
}

src/Migration/Source.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ public function getBackupsBatchSize(): int
5656
return static::$defaultBatchSize;
5757
}
5858

59+
public function getSettingsBatchSize(): int
60+
{
61+
return static::$defaultBatchSize;
62+
}
63+
5964
/**
6065
* @param array<Resource> $resources
6166
* @return void
@@ -121,6 +126,7 @@ public function exportResources(array $resources): void
121126
Transfer::GROUP_SITES => Transfer::GROUP_SITES_RESOURCES,
122127
Transfer::GROUP_INTEGRATIONS => Transfer::GROUP_INTEGRATIONS_RESOURCES,
123128
Transfer::GROUP_BACKUPS => Transfer::GROUP_BACKUPS_RESOURCES,
129+
Transfer::GROUP_SETTINGS => Transfer::GROUP_SETTINGS_RESOURCES,
124130
];
125131

126132
foreach ($mapping as $group => $resources) {
@@ -161,6 +167,9 @@ public function exportResources(array $resources): void
161167
case Transfer::GROUP_BACKUPS:
162168
$this->exportGroupBackups($this->getBackupsBatchSize(), $resources);
163169
break;
170+
case Transfer::GROUP_SETTINGS:
171+
$this->exportGroupSettings($this->getSettingsBatchSize(), $resources);
172+
break;
164173
}
165174
}
166175
}
@@ -228,4 +237,12 @@ abstract protected function exportGroupIntegrations(int $batchSize, array $resou
228237
* @param array<string> $resources Resources to export
229238
*/
230239
abstract protected function exportGroupBackups(int $batchSize, array $resources): void;
240+
241+
/**
242+
* Export Settings Group
243+
*
244+
* @param int $batchSize
245+
* @param array<string> $resources Resources to export
246+
*/
247+
abstract protected function exportGroupSettings(int $batchSize, array $resources): void;
231248
}

src/Migration/Sources/Appwrite.php

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
use Utopia\Migration\Resources\Messaging\Provider;
6262
use Utopia\Migration\Resources\Messaging\Subscriber;
6363
use Utopia\Migration\Resources\Messaging\Topic;
64+
use Utopia\Migration\Resources\Settings\ProjectVariable;
6465
use Utopia\Migration\Resources\Sites\Deployment as SiteDeployment;
6566
use Utopia\Migration\Resources\Sites\EnvVar as SiteEnvVar;
6667
use Utopia\Migration\Resources\Sites\Site;
@@ -214,6 +215,7 @@ public static function getSupportedResources(): array
214215
Resource::TYPE_BACKUP_POLICY,
215216

216217
// Settings
218+
Resource::TYPE_PROJECT_VARIABLE,
217219
];
218220
}
219221

@@ -254,6 +256,7 @@ public function report(array $resources = [], array $resourceIds = []): array
254256
$this->reportSites($resources, $report, $resourceIds);
255257
$this->reportIntegrations($resources, $report, $resourceIds);
256258
$this->reportBackups($resources, $report, $resourceIds);
259+
$this->reportSettings($resources, $report, $resourceIds);
257260

258261
$report['version'] = $this->call(
259262
'GET',
@@ -1449,6 +1452,90 @@ protected function reportBackups(array $resources, array &$report, array $resour
14491452
}
14501453
}
14511454

1455+
private function reportSettings(array $resources, array &$report, array $resourceIds = []): void
1456+
{
1457+
if (\in_array(Resource::TYPE_PROJECT_VARIABLE, $resources)) {
1458+
$variableQueries = $this->buildQueries(
1459+
resourceType: Resource::TYPE_PROJECT_VARIABLE,
1460+
resourceIds: $resourceIds,
1461+
limit: 1
1462+
);
1463+
try {
1464+
$report[Resource::TYPE_PROJECT_VARIABLE] = $this->project->listVariables($variableQueries)->total;
1465+
} catch (\Throwable) {
1466+
$report[Resource::TYPE_PROJECT_VARIABLE] = 0;
1467+
}
1468+
}
1469+
}
1470+
1471+
/**
1472+
* @param int $batchSize
1473+
* @param array<string> $resources
1474+
*/
1475+
protected function exportGroupSettings(int $batchSize, array $resources): void
1476+
{
1477+
if (\in_array(Resource::TYPE_PROJECT_VARIABLE, $resources)) {
1478+
try {
1479+
$this->exportProjectVariables($batchSize);
1480+
} catch (\Throwable $e) {
1481+
$this->addError(new Exception(
1482+
Resource::TYPE_PROJECT_VARIABLE,
1483+
Transfer::GROUP_SETTINGS,
1484+
message: $e->getMessage(),
1485+
code: $e->getCode(),
1486+
previous: $e
1487+
));
1488+
}
1489+
}
1490+
}
1491+
1492+
/**
1493+
* @throws AppwriteException
1494+
*/
1495+
private function exportProjectVariables(int $batchSize): void
1496+
{
1497+
$lastId = null;
1498+
1499+
while (true) {
1500+
$queries = [Query::limit($batchSize)];
1501+
1502+
if ($this->rootResourceId !== '' && $this->rootResourceType === Resource::TYPE_PROJECT_VARIABLE) {
1503+
$queries[] = Query::equal('$id', $this->rootResourceId);
1504+
$queries[] = Query::limit(1);
1505+
}
1506+
1507+
if ($lastId !== null) {
1508+
$queries[] = Query::cursorAfter($lastId);
1509+
}
1510+
1511+
$response = $this->project->listVariables($queries);
1512+
if ($response->total === 0) {
1513+
break;
1514+
}
1515+
1516+
$variables = [];
1517+
1518+
foreach ($response->variables as $variable) {
1519+
$variables[] = new ProjectVariable(
1520+
$variable->id,
1521+
$variable->key,
1522+
$variable->value,
1523+
$variable->secret,
1524+
createdAt: $variable->createdAt,
1525+
updatedAt: $variable->updatedAt,
1526+
);
1527+
1528+
$lastId = $variable->id;
1529+
}
1530+
1531+
$this->callback($variables);
1532+
1533+
if (\count($response->variables) < $batchSize) {
1534+
break;
1535+
}
1536+
}
1537+
}
1538+
14521539
/**
14531540
* @throws AppwriteException
14541541
*/

src/Migration/Sources/CSV.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,11 @@ protected function exportGroupBackups(int $batchSize, array $resources): void
440440
throw new \Exception('Not Implemented');
441441
}
442442

443+
protected function exportGroupSettings(int $batchSize, array $resources): void
444+
{
445+
throw new \Exception('Not Implemented');
446+
}
447+
443448
/**
444449
* @param callable(resource $stream, string $delimiter): void $callback
445450
* @return void

src/Migration/Sources/Firebase.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,4 +827,9 @@ protected function exportGroupBackups(int $batchSize, array $resources): void
827827
{
828828
throw new \Exception('Not implemented');
829829
}
830+
831+
protected function exportGroupSettings(int $batchSize, array $resources): void
832+
{
833+
throw new \Exception('Not implemented');
834+
}
830835
}

src/Migration/Sources/JSON.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ protected function exportGroupBackups(int $batchSize, array $resources): void
214214
throw new \Exception('Not Implemented');
215215
}
216216

217+
protected function exportGroupSettings(int $batchSize, array $resources): void
218+
{
219+
throw new \Exception('Not Implemented');
220+
}
221+
217222
/**
218223
* @throws \Exception
219224
*/

src/Migration/Sources/NHost.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,4 +962,9 @@ protected function exportGroupBackups(int $batchSize, array $resources): void
962962
{
963963
throw new \Exception('Not Implemented');
964964
}
965+
966+
protected function exportGroupSettings(int $batchSize, array $resources): void
967+
{
968+
throw new \Exception('Not Implemented');
969+
}
965970
}

0 commit comments

Comments
 (0)