mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
36
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6793dc0b5 | ||
|
|
a2ad25a00a | ||
|
|
16ad05792d | ||
|
|
e5f0bc2df6 | ||
|
|
0d3d14d9e1 | ||
|
|
f2826189c6 | ||
|
|
8b026d3459 | ||
|
|
2306072dda | ||
|
|
91edf82060 | ||
|
|
3fc50e4d8d | ||
|
|
71dff441ed | ||
|
|
65780d75f9 | ||
|
|
ee107d30b3 | ||
|
|
499ca71c28 | ||
|
|
cfe60dd2a2 | ||
|
|
ab85942276 | ||
|
|
0214f22d20 | ||
|
|
e0269e268f | ||
|
|
a4844599c6 | ||
|
|
ce876adb3a | ||
|
|
59edcf1934 | ||
|
|
5821832da6 | ||
|
|
d4df2c51de | ||
|
|
dbbc57bc37 | ||
|
|
d772220414 | ||
|
|
5510231b8e | ||
|
|
152de6c584 | ||
|
|
161c5af66f | ||
|
|
c497a68613 | ||
|
|
50f9c67862 | ||
|
|
198f9a64a3 | ||
|
|
300aaeb251 | ||
|
|
f989ccde57 | ||
|
|
fc88d4b4ab | ||
|
|
df604cb2ef | ||
|
|
a6d58a847d |
@@ -47,6 +47,36 @@ Examples:
|
||||
'resourceType' => 'deployments'
|
||||
```
|
||||
|
||||
## Performance Patterns
|
||||
|
||||
### Document Update Optimization
|
||||
|
||||
When updating documents, always pass only the changed attributes as a sparse `Document` rather than the full document. This is more efficient because `updateDocument()` internally performs `array_merge($old, $new)`.
|
||||
|
||||
**Correct Pattern:**
|
||||
```php
|
||||
// Good: Pass only changed attributes directly
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'name' => $name,
|
||||
'email' => $email,
|
||||
]));
|
||||
```
|
||||
|
||||
**Incorrect Pattern:**
|
||||
```php
|
||||
$user->setAttribute('name', $name);
|
||||
$user->setAttribute('email', $email);
|
||||
|
||||
// Bad: Passing full document is inefficient
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
```
|
||||
|
||||
**Exceptions:**
|
||||
- Migration files (need full document updates by design)
|
||||
- Cases already using `array_merge()` with `getArrayCopy()`
|
||||
- Updates where almost all attributes of the document change at once (sparse update provides little benefit compared to passing the full document)
|
||||
- Complex nested relationship logic where full document state is required
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Critical Security Practices
|
||||
|
||||
@@ -27,7 +27,7 @@ return [
|
||||
Exception::GENERAL_RESOURCE_BLOCKED => [
|
||||
'name' => Exception::GENERAL_RESOURCE_BLOCKED,
|
||||
'description' => 'Access to this resource is blocked.',
|
||||
'code' => 401,
|
||||
'code' => 403,
|
||||
],
|
||||
Exception::GENERAL_UNKNOWN_ORIGIN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN_ORIGIN,
|
||||
@@ -168,8 +168,8 @@ return [
|
||||
],
|
||||
Exception::USER_BLOCKED => [
|
||||
'name' => Exception::USER_BLOCKED,
|
||||
'description' => 'The current user has been blocked. You can unblock the user by making a request to the User API\'s "Update User Status" endpoint or in the Appwrite Console\'s Auth section.',
|
||||
'code' => 401,
|
||||
'description' => 'The current user has been blocked.',
|
||||
'code' => 403,
|
||||
],
|
||||
Exception::USER_INVALID_TOKEN => [
|
||||
'name' => Exception::USER_INVALID_TOKEN,
|
||||
|
||||
+1
-1
@@ -231,7 +231,7 @@ return [
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
'beta' => true,
|
||||
'beta' => false,
|
||||
'dev' => false,
|
||||
'hidden' => false,
|
||||
'family' => APP_SDK_PLATFORM_CONSOLE,
|
||||
|
||||
@@ -288,7 +288,10 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
}
|
||||
|
||||
try {
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'emailVerification' => $user->getAttribute('emailVerification'),
|
||||
'phoneVerification' => $user->getAttribute('phoneVerification'),
|
||||
]));
|
||||
} catch (\Throwable $th) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB');
|
||||
}
|
||||
@@ -1032,7 +1035,11 @@ Http::post('/v1/account/sessions/email')
|
||||
->setAttribute('password', $proofForPasswordUpdated->hash($password))
|
||||
->setAttribute('hash', $proofForPasswordUpdated->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPasswordUpdated->getHash()->getOptions());
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'password' => $user->getAttribute('password'),
|
||||
'hash' => $user->getAttribute('hash'),
|
||||
'hashOptions' => $user->getAttribute('hashOptions'),
|
||||
]));
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
@@ -1822,7 +1829,11 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->setAttribute('providerAccessToken', $accessToken)
|
||||
->setAttribute('providerRefreshToken', $refreshToken)
|
||||
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int) $accessTokenExpiry));
|
||||
$dbForProject->updateDocument('identities', $identity->getId(), $identity);
|
||||
$dbForProject->updateDocument('identities', $identity->getId(), new Document([
|
||||
'providerAccessToken' => $identity->getAttribute('providerAccessToken'),
|
||||
'providerRefreshToken' => $identity->getAttribute('providerRefreshToken'),
|
||||
'providerAccessTokenExpiry' => $identity->getAttribute('providerAccessTokenExpiry'),
|
||||
]));
|
||||
}
|
||||
|
||||
if (empty($user->getAttribute('email'))) {
|
||||
@@ -1960,7 +1971,10 @@ Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->setAttribute('sessionId', $session->getId())
|
||||
->setAttribute('sessionInternalId', $session->getSequence());
|
||||
|
||||
$dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||
$dbForProject->updateDocument('targets', $target->getId(), new Document([
|
||||
'sessionId' => $target->getAttribute('sessionId'),
|
||||
'sessionInternalId' => $target->getAttribute('sessionInternalId'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3145,7 +3159,9 @@ Http::patch('/v1/account/name')
|
||||
|
||||
$user->setAttribute('name', $name);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'name' => $user->getAttribute('name'),
|
||||
]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -3798,13 +3814,15 @@ Http::put('/v1/account/recovery')
|
||||
|
||||
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, true]);
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile
|
||||
->setAttribute('password', $newPassword)
|
||||
->setAttribute('passwordHistory', $history)
|
||||
->setAttribute('passwordUpdate', DateTime::now())
|
||||
->setAttribute('hash', $proofForPassword->getHash()->getName())
|
||||
->setAttribute('hashOptions', $proofForPassword->getHash()->getOptions())
|
||||
->setAttribute('emailVerification', true));
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), new Document(
|
||||
[
|
||||
'password' => $newPassword,
|
||||
'passwordHistory' => $history,
|
||||
'passwordUpdate' => DateTime::now(),
|
||||
'hash' => $proofForPassword->getHash()->getName(),
|
||||
'hashOptions' => $proofForPassword->getHash()->getOptions(),
|
||||
'emailVerification' => true]
|
||||
));
|
||||
|
||||
$user->setAttributes($profile->getArrayCopy());
|
||||
|
||||
@@ -4126,7 +4144,7 @@ Http::put('/v1/account/verifications/email')
|
||||
|
||||
$authorization->addRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), new Document(['emailVerification' => true]));
|
||||
|
||||
$user->setAttributes($profile->getArrayCopy());
|
||||
|
||||
@@ -4342,7 +4360,7 @@ Http::put('/v1/account/verifications/phone')
|
||||
|
||||
$authorization->addRole(Role::user($profile->getId())->toString());
|
||||
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true));
|
||||
$profile = $dbForProject->updateDocument('users', $profile->getId(), new Document(['phoneVerification' => true]));
|
||||
|
||||
$user->setAttributes($profile->getArrayCopy());
|
||||
|
||||
@@ -4500,7 +4518,11 @@ Http::put('/v1/account/targets/:targetId/push')
|
||||
|
||||
$target->setAttribute('name', "{$device['deviceBrand']} {$device['deviceModel']}");
|
||||
|
||||
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||
$target = $dbForProject->updateDocument('targets', $target->getId(), new Document([
|
||||
'identifier' => $target->getAttribute('identifier'),
|
||||
'expired' => $target->getAttribute('expired'),
|
||||
'name' => $target->getAttribute('name'),
|
||||
]));
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
|
||||
@@ -1161,7 +1161,7 @@ Http::patch('/v1/users/:userId/status')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', (bool) $status));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['status' => (bool) $status]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId());
|
||||
@@ -1204,7 +1204,7 @@ Http::put('/v1/users/:userId/labels')
|
||||
|
||||
$user->setAttribute('labels', (array) \array_values(\array_unique($labels)));
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['labels' => $user->getAttribute('labels')]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId());
|
||||
@@ -1245,7 +1245,7 @@ Http::patch('/v1/users/:userId/verification/phone')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('phoneVerification', $phoneVerification));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['phoneVerification' => $phoneVerification]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId());
|
||||
@@ -1289,7 +1289,7 @@ Http::patch('/v1/users/:userId/name')
|
||||
|
||||
$user->setAttribute('name', $name);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['name' => $user->getAttribute('name')]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -1344,7 +1344,10 @@ Http::patch('/v1/users/:userId/password')
|
||||
->setAttribute('password', '')
|
||||
->setAttribute('passwordUpdate', DateTime::now());
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'password' => $user->getAttribute('password'),
|
||||
'passwordUpdate' => $user->getAttribute('passwordUpdate'),
|
||||
]));
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
}
|
||||
@@ -1377,7 +1380,13 @@ Http::patch('/v1/users/:userId/password')
|
||||
->setAttribute('hash', $hasher->getName())
|
||||
->setAttribute('hashOptions', $hasher->getOptions());
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'password' => $user->getAttribute('password'),
|
||||
'passwordHistory' => $user->getAttribute('passwordHistory'),
|
||||
'passwordUpdate' => $user->getAttribute('passwordUpdate'),
|
||||
'hash' => $user->getAttribute('hash'),
|
||||
'hashOptions' => $user->getAttribute('hashOptions'),
|
||||
]));
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$invalidate = $project->getAttribute('auths', default: [])['invalidateSessions'] ?? false;
|
||||
@@ -1469,7 +1478,15 @@ Http::patch('/v1/users/:userId/email')
|
||||
;
|
||||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'email' => $user->getAttribute('email'),
|
||||
'emailVerification' => $user->getAttribute('emailVerification'),
|
||||
'emailCanonical' => $user->getAttribute('emailCanonical'),
|
||||
'emailIsCanonical' => $user->getAttribute('emailIsCanonical'),
|
||||
'emailIsCorporate' => $user->getAttribute('emailIsCorporate'),
|
||||
'emailIsDisposable' => $user->getAttribute('emailIsDisposable'),
|
||||
'emailIsFree' => $user->getAttribute('emailIsFree'),
|
||||
]));
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
@@ -1477,7 +1494,8 @@ Http::patch('/v1/users/:userId/email')
|
||||
|
||||
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||
if (\strlen($email) !== 0) {
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email));
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), new Document(['identifier' => $email]));
|
||||
$oldTarget->setAttribute('identifier', $email);
|
||||
} else {
|
||||
$dbForProject->deleteDocument('targets', $oldTarget->getId());
|
||||
}
|
||||
@@ -1558,7 +1576,10 @@ Http::patch('/v1/users/:userId/phone')
|
||||
}
|
||||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'phone' => $user->getAttribute('phone'),
|
||||
'phoneVerification' => $user->getAttribute('phoneVerification'),
|
||||
]));
|
||||
/**
|
||||
* @var Document $oldTarget
|
||||
*/
|
||||
@@ -1566,7 +1587,8 @@ Http::patch('/v1/users/:userId/phone')
|
||||
|
||||
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
|
||||
if (\strlen($number) !== 0) {
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $number));
|
||||
$dbForProject->updateDocument('targets', $oldTarget->getId(), new Document(['identifier' => $number]));
|
||||
$oldTarget->setAttribute('identifier', $number);
|
||||
} else {
|
||||
$dbForProject->deleteDocument('targets', $oldTarget->getId());
|
||||
}
|
||||
@@ -1630,7 +1652,7 @@ Http::patch('/v1/users/:userId/verification')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', $emailVerification));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['emailVerification' => $emailVerification]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -1668,7 +1690,7 @@ Http::patch('/v1/users/:userId/prefs')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['prefs' => $prefs]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId());
|
||||
@@ -1768,7 +1790,13 @@ Http::patch('/v1/users/:userId/targets/:targetId')
|
||||
$target->setAttribute('name', $name);
|
||||
}
|
||||
|
||||
$target = $dbForProject->updateDocument('targets', $target->getId(), $target);
|
||||
$target = $dbForProject->updateDocument('targets', $target->getId(), new Document([
|
||||
'identifier' => $target->getAttribute('identifier'),
|
||||
'expired' => $target->getAttribute('expired'),
|
||||
'providerId' => $target->getAttribute('providerId'),
|
||||
'providerInternalId' => $target->getAttribute('providerInternalId'),
|
||||
'name' => $target->getAttribute('name'),
|
||||
]));
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
$queueForEvents
|
||||
@@ -1836,7 +1864,7 @@ Http::patch('/v1/users/:userId/mfa')
|
||||
|
||||
$user->setAttribute('mfa', $mfa);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['mfa' => $user->getAttribute('mfa')]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -2024,7 +2052,7 @@ Http::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
|
||||
$mfaRecoveryCodes = Type::generateBackupCodes();
|
||||
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document(['mfaRecoveryCodes' => $mfaRecoveryCodes]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -2096,7 +2124,7 @@ Http::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
|
||||
$mfaRecoveryCodes = Type::generateBackupCodes();
|
||||
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document(['mfaRecoveryCodes' => $mfaRecoveryCodes]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
|
||||
@@ -132,8 +132,9 @@ function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, S
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$accessedAt = $project->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||
$project->setAttribute('accessedAt', DateTime::now());
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), new Document([
|
||||
'accessedAt' => DateTime::now()
|
||||
])));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1649,7 +1650,10 @@ Http::get('/v1/ping')
|
||||
->setAttribute('pingedAt', $pingedAt);
|
||||
|
||||
$authorization->skip(function () use ($dbForPlatform, $project) {
|
||||
$dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
$dbForPlatform->updateDocument('projects', $project->getId(), new Document([
|
||||
'pingCount' => $project->getAttribute('pingCount'),
|
||||
'pingedAt' => $project->getAttribute('pingedAt')
|
||||
]));
|
||||
});
|
||||
|
||||
$queueForEvents
|
||||
|
||||
@@ -360,8 +360,9 @@ Http::init()
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$accessedAt = $project->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||
$project->setAttribute('accessedAt', DateTime::now());
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), new Document([
|
||||
'accessedAt' => DateTime::now()
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,9 +372,13 @@ Http::init()
|
||||
$user->setAttribute('accessedAt', DateTime::now());
|
||||
|
||||
if ($project->getId() !== 'console' && APP_MODE_ADMIN !== $mode) {
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document([
|
||||
'accessedAt' => $user->getAttribute('accessedAt')
|
||||
]));
|
||||
} else {
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('users', $user->getId(), $user));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('users', $user->getId(), new Document([
|
||||
'accessedAt' => $user->getAttribute('accessedAt')
|
||||
])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -649,7 +654,9 @@ Http::init()
|
||||
$transformedAt = $file->getAttribute('transformedAt', '');
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
|
||||
$file->setAttribute('transformedAt', DateTime::now());
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), new Document([
|
||||
'transformedAt' => $file->getAttribute('transformedAt')
|
||||
])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -948,7 +955,9 @@ Http::shutdown()
|
||||
}
|
||||
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
|
||||
$cacheLog->setAttribute('accessedAt', $now);
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), new Document([
|
||||
'accessedAt' => $cacheLog->getAttribute('accessedAt')
|
||||
])));
|
||||
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
|
||||
$cache->save($key, $data['payload']);
|
||||
}
|
||||
|
||||
+10
-3
@@ -1235,7 +1235,9 @@ Http::setResource('devKey', function (Request $request, Document $project, array
|
||||
$accessedAt = $key->getAttribute('accessedAt', 0);
|
||||
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
|
||||
$key->setAttribute('accessedAt', DatabaseDateTime::now());
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), new Document([
|
||||
'accessedAt' => $key->getAttribute('accessedAt')
|
||||
])));
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
|
||||
@@ -1252,7 +1254,10 @@ Http::setResource('devKey', function (Request $request, Document $project, array
|
||||
|
||||
/** Update access time as well */
|
||||
$key->setAttribute('accessedAt', DatabaseDateTime::now());
|
||||
$key = $authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
|
||||
$key = $authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), new Document([
|
||||
'sdks' => $key->getAttribute('sdks'),
|
||||
'accessedAt' => $key->getAttribute('accessedAt')
|
||||
])));
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
}
|
||||
}
|
||||
@@ -1409,7 +1414,9 @@ Http::setResource('resourceToken', function ($project, $dbForProject, $request,
|
||||
$accessedAt = $token->getAttribute('accessedAt', 0);
|
||||
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
|
||||
$token->setAttribute('accessedAt', DatabaseDateTime::now());
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), new Document([
|
||||
'accessedAt' => $token->getAttribute('accessedAt')
|
||||
])));
|
||||
}
|
||||
|
||||
return new Document([
|
||||
|
||||
+4
-1
@@ -356,7 +356,10 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
||||
->setAttribute('timestamp', DateTime::now())
|
||||
->setAttribute('value', json_encode($payload));
|
||||
|
||||
$database->getAuthorization()->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
|
||||
$database->getAuthorization()->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), new Document([
|
||||
'timestamp' => $statsDocument->getAttribute('timestamp'),
|
||||
'value' => $statsDocument->getAttribute('value')
|
||||
])));
|
||||
} catch (Throwable $th) {
|
||||
logError($th, "updateWorkerDocument");
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
"swoole/ide-helper": "6.*",
|
||||
"phpstan/phpstan": "1.12.*",
|
||||
"textalk/websocket": "1.5.*",
|
||||
"czproject/git-php": "4.*",
|
||||
"laravel/pint": "1.*",
|
||||
"phpbench/phpbench": "1.*"
|
||||
},
|
||||
|
||||
Generated
+66
-2
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "1fb043a556550f62ab27a0aad0b9332a",
|
||||
"content-hash": "1cc64e07484256225f56bd525674c3b8",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
@@ -5580,6 +5580,70 @@
|
||||
],
|
||||
"time": "2026-02-25T14:53:45+00:00"
|
||||
},
|
||||
{
|
||||
"name": "czproject/git-php",
|
||||
"version": "v4.6.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/czproject/git-php.git",
|
||||
"reference": "1f1ecc92aea9ee31120f4f5b759f5aa947420b0a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/czproject/git-php/zipball/1f1ecc92aea9ee31120f4f5b759f5aa947420b0a",
|
||||
"reference": "1f1ecc92aea9ee31120f4f5b759f5aa947420b0a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "8.0 - 8.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/tester": "^2.5"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Jan Pecha",
|
||||
"email": "janpecha@email.cz"
|
||||
}
|
||||
],
|
||||
"description": "Library for work with Git repository in PHP.",
|
||||
"keywords": [
|
||||
"git"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/czproject/git-php/issues",
|
||||
"source": "https://github.com/czproject/git-php/tree/v4.6.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/sponsors/janpecha",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://www.janpecha.cz/donate/git-php/",
|
||||
"type": "other"
|
||||
},
|
||||
{
|
||||
"url": "https://donate.stripe.com/7sIcO2a9maTSg2A9AA",
|
||||
"type": "stripe"
|
||||
},
|
||||
{
|
||||
"url": "https://thanks.dev/u/gh/czproject",
|
||||
"type": "thanks.dev"
|
||||
}
|
||||
],
|
||||
"time": "2025-11-10T07:24:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
"version": "2.0.2",
|
||||
@@ -9043,7 +9107,7 @@
|
||||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "dev",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {},
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
||||
@@ -152,7 +152,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_SMTP_HOST
|
||||
- _APP_SMTP_PORT
|
||||
- _APP_SMTP_SECURE
|
||||
@@ -305,7 +304,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_USAGE_STATS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_LOGGING_CONFIG_REALTIME
|
||||
@@ -340,7 +338,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
@@ -371,7 +368,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
@@ -414,7 +410,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_S3_SECRET
|
||||
@@ -474,7 +469,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_WORKERS_NUM
|
||||
- _APP_QUEUE_NAME
|
||||
@@ -513,7 +507,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_VCS_GITHUB_APP_NAME
|
||||
- _APP_VCS_GITHUB_PRIVATE_KEY
|
||||
@@ -658,7 +651,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
@@ -722,7 +714,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_FUNCTIONS_TIMEOUT
|
||||
- _APP_SITES_TIMEOUT
|
||||
- _APP_COMPUTE_BUILD_TIMEOUT
|
||||
@@ -808,7 +799,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_SMS_FROM
|
||||
- _APP_SMS_PROVIDER
|
||||
@@ -875,7 +865,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
@@ -918,7 +907,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_MAINTENANCE_INTERVAL
|
||||
- _APP_MAINTENANCE_RETENTION_EXECUTION
|
||||
- _APP_MAINTENANCE_RETENTION_CACHE
|
||||
@@ -994,7 +982,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
@@ -1028,7 +1015,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
@@ -1062,7 +1048,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
@@ -1100,7 +1085,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-task-scheduler-executions:
|
||||
@@ -1131,7 +1115,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
|
||||
appwrite-task-scheduler-messages:
|
||||
entrypoint: schedule-messages
|
||||
@@ -1161,7 +1144,6 @@ services:
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_DB_ADAPTER
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-assistant:
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
colors="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
stopOnError="false"
|
||||
cacheDirectory=".phpunit.cache"
|
||||
>
|
||||
<extensions>
|
||||
|
||||
@@ -119,7 +119,7 @@ class Update extends Action
|
||||
|
||||
$authenticator->setAttribute('verified', true);
|
||||
|
||||
$dbForProject->updateDocument('authenticators', $authenticator->getId(), $authenticator);
|
||||
$dbForProject->updateDocument('authenticators', $authenticator->getId(), new Document(['verified' => true]));
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
$factors = $session->getAttribute('factors', []);
|
||||
@@ -127,7 +127,7 @@ class Update extends Action
|
||||
$factors = \array_values(\array_unique($factors));
|
||||
|
||||
$session->setAttribute('factors', $factors);
|
||||
$dbForProject->updateDocument('sessions', $session->getId(), $session);
|
||||
$dbForProject->updateDocument('sessions', $session->getId(), new Document(['factors' => $factors]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ class Update extends Action
|
||||
$mfaRecoveryCodes = \array_diff($mfaRecoveryCodes, [$otp]);
|
||||
$mfaRecoveryCodes = \array_values($mfaRecoveryCodes);
|
||||
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document(['mfaRecoveryCodes' => $mfaRecoveryCodes]));
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -147,11 +147,13 @@ class Update extends Action
|
||||
$factors[] = $type;
|
||||
$factors = \array_values(\array_unique($factors));
|
||||
|
||||
$mfaUpdatedAt = DateTime::now();
|
||||
|
||||
$session
|
||||
->setAttribute('factors', $factors)
|
||||
->setAttribute('mfaUpdatedAt', DateTime::now());
|
||||
->setAttribute('mfaUpdatedAt', $mfaUpdatedAt);
|
||||
|
||||
$dbForProject->updateDocument('sessions', $session->getId(), $session);
|
||||
$dbForProject->updateDocument('sessions', $session->getId(), new Document(['factors' => $factors, 'mfaUpdatedAt' => $mfaUpdatedAt]));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId())
|
||||
|
||||
@@ -93,7 +93,7 @@ class Create extends Action
|
||||
|
||||
$mfaRecoveryCodes = Type::generateBackupCodes();
|
||||
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document(['mfaRecoveryCodes' => $mfaRecoveryCodes]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class Update extends Action
|
||||
|
||||
$mfaRecoveryCodes = Type::generateBackupCodes();
|
||||
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$dbForProject->updateDocument('users', $user->getId(), new Document(['mfaRecoveryCodes' => $mfaRecoveryCodes]));
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ class Update extends Action
|
||||
): void {
|
||||
$user->setAttribute('mfa', $mfa);
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), new Document(['mfa' => $mfa]));
|
||||
|
||||
if ($mfa) {
|
||||
$factors = $session->getAttribute('factors', []);
|
||||
@@ -89,7 +89,7 @@ class Update extends Action
|
||||
$factors = \array_values(\array_unique($factors));
|
||||
|
||||
$session->setAttribute('factors', $factors);
|
||||
$dbForProject->updateDocument('sessions', $session->getId(), $session);
|
||||
$dbForProject->updateDocument('sessions', $session->getId(), new Document(['factors' => $factors]));
|
||||
}
|
||||
|
||||
$queueForEvents->setParam('userId', $user->getId());
|
||||
|
||||
@@ -143,7 +143,12 @@ class Base extends Action
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
@@ -249,7 +254,12 @@ class Base extends Action
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
|
||||
$sitesDomain = $platform['sitesDomain'];
|
||||
$domain = ID::unique() . "." . $sitesDomain;
|
||||
|
||||
+5
-2
@@ -12,6 +12,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\IndexDependency as IndexDependencyValidator;
|
||||
use Utopia\Database\Validator\Key;
|
||||
@@ -98,7 +99,8 @@ class Delete extends Action
|
||||
}
|
||||
|
||||
if ($attribute->getAttribute('status') === 'available') {
|
||||
$attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'deleting'));
|
||||
$attribute->setAttribute('status', 'deleting');
|
||||
$attribute = $dbForProject->updateDocument('attributes', $attribute->getId(), new Document(['status' => 'deleting']));
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId);
|
||||
@@ -118,7 +120,8 @@ class Delete extends Action
|
||||
}
|
||||
|
||||
if ($relatedAttribute->getAttribute('status') === 'available') {
|
||||
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'deleting'));
|
||||
$relatedAttribute->setAttribute('status', 'deleting');
|
||||
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), new Document(['status' => 'deleting']));
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $options['relatedCollection']);
|
||||
|
||||
+3
-1
@@ -12,6 +12,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
@@ -96,7 +97,8 @@ class Delete extends Action
|
||||
|
||||
// Only update status if removing available index
|
||||
if ($index->getAttribute('status') === 'available') {
|
||||
$index = $dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'deleting'));
|
||||
$index->setAttribute('status', 'deleting');
|
||||
$index = $dbForProject->updateDocument('indexes', $index->getId(), new Document(['status' => 'deleting']));
|
||||
}
|
||||
|
||||
$dbForProject->purgeCachedDocument('database_' . $db->getSequence(), $collectionId);
|
||||
|
||||
@@ -322,13 +322,19 @@ class Databases extends Action
|
||||
$dbForProject->updateDocument(
|
||||
'attributes',
|
||||
$attribute->getId(),
|
||||
$attribute->setAttribute('status', 'stuck')
|
||||
new Document([
|
||||
'error' => $attribute->getAttribute('error'),
|
||||
'status' => 'stuck',
|
||||
])
|
||||
);
|
||||
if (!$relatedAttribute->isEmpty()) {
|
||||
$dbForProject->updateDocument(
|
||||
'attributes',
|
||||
$relatedAttribute->getId(),
|
||||
$relatedAttribute->setAttribute('status', 'stuck')
|
||||
new Document([
|
||||
'error' => $relatedAttribute->getAttribute('error'),
|
||||
'status' => 'stuck',
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
@@ -382,7 +388,11 @@ class Databases extends Action
|
||||
if ($exists) { // Delete the duplicate if created, else update in db
|
||||
$this->deleteIndex($database, $collection, $index, $project, $dbForPlatform, $dbForProject, $queueForRealtime);
|
||||
} else {
|
||||
$dbForProject->updateDocument('indexes', $index->getId(), $index);
|
||||
$dbForProject->updateDocument('indexes', $index->getId(), new Document([
|
||||
'attributes' => $index->getAttribute('attributes'),
|
||||
'lengths' => $index->getAttribute('lengths'),
|
||||
'orders' => $index->getAttribute('orders'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +227,9 @@ class Create extends Action
|
||||
|
||||
foreach ($activeDeployments as $activeDeployment) {
|
||||
$activeDeployment->setAttribute('activate', false);
|
||||
$dbForProject->updateDocument('deployments', $activeDeployment->getId(), $activeDeployment);
|
||||
$dbForProject->updateDocument('deployments', $activeDeployment->getId(), new Document([
|
||||
'activate' => false,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,14 +257,17 @@ class Create extends Action
|
||||
'type' => $type
|
||||
]));
|
||||
|
||||
$function = $function
|
||||
->setAttribute('latestDeploymentId', $deployment->getId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
} else {
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('sourceSize', $fileSize)->setAttribute('sourceMetadata', $metadata));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
|
||||
'sourceSize' => $fileSize,
|
||||
'sourceMetadata' => $metadata,
|
||||
]));
|
||||
}
|
||||
|
||||
// Start the build
|
||||
@@ -295,14 +300,17 @@ class Create extends Action
|
||||
'type' => $type
|
||||
]));
|
||||
|
||||
$function = $function
|
||||
->setAttribute('latestDeploymentId', $deployment->getId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
} else {
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('sourceChunksUploaded', $chunksUploaded)->setAttribute('sourceMetadata', $metadata));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
|
||||
'sourceChunksUploaded' => $chunksUploaded,
|
||||
'sourceMetadata' => $metadata,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,11 +107,12 @@ class Delete extends Action
|
||||
$function = $dbForProject->updateDocument(
|
||||
'functions',
|
||||
$function->getId(),
|
||||
$function
|
||||
->setAttribute('latestDeploymentCreatedAt', $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getSequence())
|
||||
->setAttribute('latestDeploymentId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getId())
|
||||
->setAttribute('latestDeploymentStatus', $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', ''))
|
||||
new Document([
|
||||
'latestDeploymentCreatedAt' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt(),
|
||||
'latestDeploymentInternalId' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getSequence(),
|
||||
'latestDeploymentId' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getId(),
|
||||
'latestDeploymentStatus' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', ''),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -119,7 +120,12 @@ class Create extends Action
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $function->getAttribute('latestDeploymentId'),
|
||||
'latestDeploymentInternalId' => $function->getAttribute('latestDeploymentInternalId'),
|
||||
'latestDeploymentCreatedAt' => $function->getAttribute('latestDeploymentCreatedAt'),
|
||||
'latestDeploymentStatus' => $function->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
|
||||
@@ -91,7 +91,7 @@ class Update extends Action
|
||||
$endTime = new \DateTime('now');
|
||||
$duration = $endTime->getTimestamp() - $startTime->getTimestamp();
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttributes([
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildEndedAt' => DateTime::now(),
|
||||
'buildDuration' => $duration,
|
||||
'status' => 'canceled'
|
||||
@@ -99,7 +99,9 @@ class Update extends Action
|
||||
|
||||
if ($deployment->getSequence() === $function->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$function = $function->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentStatus' => $function->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -174,7 +174,12 @@ class Create extends Base
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $function->getAttribute('latestDeploymentId'),
|
||||
'latestDeploymentInternalId' => $function->getAttribute('latestDeploymentInternalId'),
|
||||
'latestDeploymentCreatedAt' => $function->getAttribute('latestDeploymentCreatedAt'),
|
||||
'latestDeploymentStatus' => $function->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
|
||||
|
||||
$this->updateEmptyManualRule($project, $function, $deployment, $dbForPlatform, $authorization);
|
||||
|
||||
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
@@ -119,7 +120,10 @@ class Delete extends Base
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('active', false);
|
||||
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
])));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -283,7 +283,12 @@ class Create extends Base
|
||||
$function->setAttribute('repositoryInternalId', $repository->getSequence());
|
||||
}
|
||||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'scheduleId' => $function->getAttribute('scheduleId'),
|
||||
'scheduleInternalId' => $function->getAttribute('scheduleInternalId'),
|
||||
'repositoryId' => $function->getAttribute('repositoryId'),
|
||||
'repositoryInternalId' => $function->getAttribute('repositoryInternalId'),
|
||||
]));
|
||||
|
||||
// Backwards compatibility with 1.6 behaviour
|
||||
$requestFormat = $request->getHeader('x-appwrite-response-format', System::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
|
||||
@@ -321,12 +326,12 @@ class Create extends Base
|
||||
referenceType: 'branch'
|
||||
);
|
||||
|
||||
$function = $function
|
||||
->setAttribute('latestDeploymentId', $deployment->getId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
} elseif (!$template->isEmpty()) {
|
||||
// Deploy non-VCS from template
|
||||
$deploymentId = ID::unique();
|
||||
@@ -347,12 +352,12 @@ class Create extends Base
|
||||
'activate' => true,
|
||||
]));
|
||||
|
||||
$function = $function
|
||||
->setAttribute('latestDeploymentId', $deployment->getId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
|
||||
@@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -90,7 +91,10 @@ class Delete extends Base
|
||||
$schedule
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('active', false);
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
])));
|
||||
}
|
||||
|
||||
$queueForDeletes
|
||||
|
||||
@@ -103,7 +103,11 @@ class Update extends Base
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'schedule' => $schedule->getAttribute('schedule'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
])));
|
||||
|
||||
$queries = [
|
||||
Query::equal('trigger', ['manual']),
|
||||
@@ -119,7 +123,10 @@ class Update extends Base
|
||||
->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'deploymentId' => $rule->getAttribute('deploymentId'),
|
||||
'deploymentInternalId' => $rule->getAttribute('deploymentInternalId'),
|
||||
])));
|
||||
}, $queries));
|
||||
|
||||
$queueForEvents
|
||||
|
||||
@@ -105,7 +105,8 @@ class Create extends Base
|
||||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
$function->setAttribute('live', false);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document(['live' => false]));
|
||||
|
||||
// Inform scheduler to pull the latest changes
|
||||
$schedule = $dbForPlatform->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
@@ -113,7 +114,11 @@ class Create extends Base
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'schedule' => $schedule->getAttribute('schedule'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
])));
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
|
||||
@@ -11,6 +11,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -86,7 +87,8 @@ class Delete extends Base
|
||||
|
||||
$dbForProject->deleteDocument('variables', $variable->getId());
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
$function->setAttribute('live', false);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document(['live' => false]));
|
||||
|
||||
// Inform scheduler to pull the latest changes
|
||||
$schedule = $dbForPlatform->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
@@ -94,7 +96,11 @@ class Delete extends Base
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'schedule' => $schedule->getAttribute('schedule'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
])));
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
@@ -99,12 +100,18 @@ class Update extends Base
|
||||
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key, 'function']));
|
||||
|
||||
try {
|
||||
$dbForProject->updateDocument('variables', $variable->getId(), $variable);
|
||||
$dbForProject->updateDocument('variables', $variable->getId(), new Document([
|
||||
'key' => $key,
|
||||
'value' => $value ?? $variable->getAttribute('value'),
|
||||
'secret' => $secret ?? $variable->getAttribute('secret'),
|
||||
'search' => implode(' ', [$variableId, $function->getId(), $key, 'function']),
|
||||
]));
|
||||
} catch (DuplicateException $th) {
|
||||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('live', false));
|
||||
$function->setAttribute('live', false);
|
||||
$dbForProject->updateDocument('functions', $function->getId(), new Document(['live' => false]));
|
||||
|
||||
// Inform scheduler to pull the latest changes
|
||||
$schedule = $dbForPlatform->getDocument('schedules', $function->getAttribute('scheduleId'));
|
||||
@@ -112,7 +119,11 @@ class Update extends Base
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $function->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deploymentId')));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'schedule' => $schedule->getAttribute('schedule'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
])));
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
}
|
||||
|
||||
@@ -279,7 +279,10 @@ class Builds extends Action
|
||||
|
||||
$deployment->setAttribute('buildStartedAt', $startTime);
|
||||
$deployment->setAttribute('status', 'processing');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildStartedAt' => $startTime,
|
||||
'status' => 'processing',
|
||||
]));
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
@@ -366,7 +369,11 @@ class Builds extends Action
|
||||
->setAttribute('sourcePath', $source)
|
||||
->setAttribute('sourceSize', $directorySize)
|
||||
->setAttribute('totalSize', $directorySize);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'sourcePath' => $deployment->getAttribute('sourcePath'),
|
||||
'sourceSize' => $deployment->getAttribute('sourceSize'),
|
||||
'totalSize' => $deployment->getAttribute('totalSize'),
|
||||
]));
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
@@ -480,7 +487,13 @@ class Builds extends Action
|
||||
$deployment->setAttribute('providerCommitAuthor', APP_VCS_GITHUB_USERNAME);
|
||||
$deployment->setAttribute('providerCommitMessage', "Create '" . $resource->getAttribute('name', '') . "' function");
|
||||
$deployment->setAttribute('providerCommitUrl', "https://github.com/$cloneOwner/$cloneRepository/commit/$providerCommitHash");
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'providerCommitHash' => $deployment->getAttribute('providerCommitHash'),
|
||||
'providerCommitAuthorUrl' => $deployment->getAttribute('providerCommitAuthorUrl'),
|
||||
'providerCommitAuthor' => $deployment->getAttribute('providerCommitAuthor'),
|
||||
'providerCommitMessage' => $deployment->getAttribute('providerCommitMessage'),
|
||||
'providerCommitUrl' => $deployment->getAttribute('providerCommitUrl'),
|
||||
]));
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
@@ -528,7 +541,11 @@ class Builds extends Action
|
||||
->setAttribute('sourcePath', $source)
|
||||
->setAttribute('sourceSize', $directorySize)
|
||||
->setAttribute('totalSize', $directorySize);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'sourcePath' => $deployment->getAttribute('sourcePath'),
|
||||
'sourceSize' => $deployment->getAttribute('sourceSize'),
|
||||
'totalSize' => $deployment->getAttribute('totalSize'),
|
||||
]));
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
@@ -543,7 +560,9 @@ class Builds extends Action
|
||||
|
||||
/** Request the executor to build the code... */
|
||||
$deployment->setAttribute('status', 'building');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'status' => 'building',
|
||||
]));
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
@@ -819,7 +838,9 @@ class Builds extends Action
|
||||
|
||||
if ($affected) {
|
||||
$deployment = $deployment->setAttribute('buildLogs', $currentLogs);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildLogs' => $currentLogs,
|
||||
]));
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
@@ -905,7 +926,14 @@ class Builds extends Action
|
||||
}
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildPath' => $deployment->getAttribute('buildPath'),
|
||||
'buildSize' => $deployment->getAttribute('buildSize'),
|
||||
'totalSize' => $deployment->getAttribute('totalSize'),
|
||||
'buildLogs' => $deployment->getAttribute('buildLogs'),
|
||||
'adapter' => $deployment->getAttribute('adapter'),
|
||||
'fallbackFile' => $deployment->getAttribute('fallbackFile'),
|
||||
]));
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
->trigger();
|
||||
@@ -916,12 +944,15 @@ class Builds extends Action
|
||||
|
||||
$logs = $deployment->getAttribute('buildLogs', '');
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][32m Deployment finished. [0m\n";
|
||||
$logs .= "\033[90m[$date] \033[90m[\033[0mappwrite\033[90m]\033[32m Deployment finished. \033[0m\n";
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
|
||||
/** Update the status */
|
||||
$deployment->setAttribute('status', 'ready');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
|
||||
'buildLogs' => $deployment->getAttribute('buildLogs'),
|
||||
'status' => 'ready',
|
||||
]));
|
||||
|
||||
Console::log('Status marked as ready');
|
||||
|
||||
@@ -1109,7 +1140,11 @@ class Builds extends Action
|
||||
->setAttribute('resourceUpdatedAt', DateTime::now())
|
||||
->setAttribute('schedule', $resource->getAttribute('schedule'))
|
||||
->setAttribute('active', !empty($resource->getAttribute('schedule')) && !empty($resource->getAttribute('deploymentId')));
|
||||
$dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule);
|
||||
$dbForPlatform->updateDocument('schedules', $schedule->getId(), new Document([
|
||||
'resourceUpdatedAt' => $schedule->getAttribute('resourceUpdatedAt'),
|
||||
'schedule' => $schedule->getAttribute('schedule'),
|
||||
'active' => $schedule->getAttribute('active'),
|
||||
]));
|
||||
}
|
||||
|
||||
/** Screenshot site */
|
||||
@@ -1160,7 +1195,12 @@ class Builds extends Action
|
||||
$deployment->setAttribute('status', 'failed');
|
||||
|
||||
$deployment->setAttribute('buildLogs', $message);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
|
||||
'buildEndedAt' => $deployment->getAttribute('buildEndedAt'),
|
||||
'buildDuration' => $deployment->getAttribute('buildDuration'),
|
||||
'status' => 'failed',
|
||||
'buildLogs' => $message,
|
||||
]));
|
||||
|
||||
if ($deployment->getSequence() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document(['latestDeploymentStatus' => $deployment->getAttribute('status', '')]));
|
||||
@@ -1467,7 +1507,9 @@ class Builds extends Action
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][33m Git action failed. Deployment will continue. [0m\n";
|
||||
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildLogs' => $deployment->getAttribute('buildLogs'),
|
||||
]));
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
@@ -1483,10 +1525,12 @@ class Builds extends Action
|
||||
|
||||
$logs = $deployment->getAttribute('buildLogs', '');
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][33m Build has been canceled. [0m\n";
|
||||
$logs .= "\033[90m[$date] \033[90m[\033[0mappwrite\033[90m]\033[33m Build has been canceled. \033[0m\n";
|
||||
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildLogs' => $deployment->getAttribute('buildLogs'),
|
||||
]));
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
|
||||
@@ -9,6 +9,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -73,7 +74,7 @@ class Update extends Action
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('expire', $expire);
|
||||
|
||||
$dbForPlatform->updateDocument('devKeys', $key->getId(), $key);
|
||||
$dbForPlatform->updateDocument('devKeys', $key->getId(), new Document(['name' => $name, 'expire' => $expire]));
|
||||
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator;
|
||||
@@ -77,9 +78,9 @@ class Update extends Action
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$project->setAttribute('labels', (array) \array_values(\array_unique($labels)));
|
||||
$labels = (array) \array_values(\array_unique($labels));
|
||||
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), new Document(['labels' => $labels]));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -72,34 +73,31 @@ class Update extends Action
|
||||
|
||||
$permissions = $this->getPermissions($teamId, $projectId);
|
||||
|
||||
$project
|
||||
->setAttribute('teamId', $teamId)
|
||||
->setAttribute('teamInternalId', $team->getSequence())
|
||||
->setAttribute('$permissions', $permissions);
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), new Document([
|
||||
'teamId' => $teamId,
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'$permissions' => $permissions,
|
||||
]));
|
||||
|
||||
$installations = $dbForPlatform->find('installations', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($installations as $installation) {
|
||||
$installation->setAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), new Document(['$permissions' => $permissions]));
|
||||
}
|
||||
|
||||
$repositories = $dbForPlatform->find('repositories', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($repositories as $repository) {
|
||||
$repository->setAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('repositories', $repository->getId(), $repository);
|
||||
$dbForPlatform->updateDocument('repositories', $repository->getId(), new Document(['$permissions' => $permissions]));
|
||||
}
|
||||
|
||||
$vcsComments = $dbForPlatform->find('vcsComments', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($vcsComments as $vcsComment) {
|
||||
$vcsComment->setAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('vcsComments', $vcsComment->getId(), $vcsComment);
|
||||
$dbForPlatform->updateDocument('vcsComments', $vcsComment->getId(), new Document(['$permissions' => $permissions]));
|
||||
}
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
|
||||
@@ -237,7 +237,7 @@ class Create extends Action
|
||||
|
||||
foreach ($activeDeployments as $activeDeployment) {
|
||||
$activeDeployment->setAttribute('activate', false);
|
||||
$dbForProject->updateDocument('deployments', $activeDeployment->getId(), $activeDeployment);
|
||||
$dbForProject->updateDocument('deployments', $activeDeployment->getId(), new Document(['activate' => false]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +272,12 @@ class Create extends Action
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'latestDeploymentId' => $deployment->getId(),
|
||||
'latestDeploymentInternalId' => $deployment->getSequence(),
|
||||
'latestDeploymentCreatedAt' => $deployment->getCreatedAt(),
|
||||
'latestDeploymentStatus' => $deployment->getAttribute('status', ''),
|
||||
]));
|
||||
|
||||
$sitesDomain = $platform['sitesDomain'];
|
||||
$domain = ID::unique() . "." . $sitesDomain;
|
||||
@@ -302,7 +307,10 @@ class Create extends Action
|
||||
]))
|
||||
);
|
||||
} else {
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('sourceSize', $fileSize)->setAttribute('sourceMetadata', $metadata));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
|
||||
'sourceSize' => $fileSize,
|
||||
'sourceMetadata' => $metadata,
|
||||
]));
|
||||
}
|
||||
|
||||
// Start the build
|
||||
@@ -342,7 +350,12 @@ class Create extends Action
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'latestDeploymentId' => $site->getAttribute('latestDeploymentId'),
|
||||
'latestDeploymentInternalId' => $site->getAttribute('latestDeploymentInternalId'),
|
||||
'latestDeploymentCreatedAt' => $site->getAttribute('latestDeploymentCreatedAt'),
|
||||
'latestDeploymentStatus' => $site->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
|
||||
$sitesDomain = $platform['sitesDomain'];
|
||||
$domain = ID::unique() . "." . $sitesDomain;
|
||||
@@ -368,7 +381,10 @@ class Create extends Action
|
||||
]))
|
||||
);
|
||||
} else {
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('sourceChunksUploaded', $chunksUploaded)->setAttribute('sourceMetadata', $metadata));
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, new Document([
|
||||
'sourceChunksUploaded' => $chunksUploaded,
|
||||
'sourceMetadata' => $metadata,
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,11 +107,12 @@ class Delete extends Action
|
||||
$site = $dbForProject->updateDocument(
|
||||
'sites',
|
||||
$site->getId(),
|
||||
$site
|
||||
->setAttribute('latestDeploymentCreatedAt', $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentInternalId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getSequence())
|
||||
->setAttribute('latestDeploymentId', $latestDeployment->isEmpty() ? '' : $latestDeployment->getId())
|
||||
->setAttribute('latestDeploymentStatus', $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', ''))
|
||||
new Document([
|
||||
'latestDeploymentCreatedAt' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getCreatedAt(),
|
||||
'latestDeploymentInternalId' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getSequence(),
|
||||
'latestDeploymentId' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getId(),
|
||||
'latestDeploymentStatus' => $latestDeployment->isEmpty() ? '' : $latestDeployment->getAttribute('status', ''),
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,12 @@ class Create extends Action
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'latestDeploymentId' => $site->getAttribute('latestDeploymentId'),
|
||||
'latestDeploymentInternalId' => $site->getAttribute('latestDeploymentInternalId'),
|
||||
'latestDeploymentCreatedAt' => $site->getAttribute('latestDeploymentCreatedAt'),
|
||||
'latestDeploymentStatus' => $site->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
|
||||
// Preview deployments for sites
|
||||
$sitesDomain = $platform['sitesDomain'];
|
||||
|
||||
@@ -89,7 +89,7 @@ class Update extends Action
|
||||
$endTime = new \DateTime('now');
|
||||
$duration = $endTime->getTimestamp() - $startTime->getTimestamp();
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttributes([
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), new Document([
|
||||
'buildEndedAt' => DateTime::now(),
|
||||
'buildDuration' => $duration,
|
||||
'status' => 'canceled'
|
||||
@@ -97,7 +97,9 @@ class Update extends Action
|
||||
|
||||
if ($deployment->getSequence() === $site->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$site = $site->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'latestDeploymentStatus' => $site->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -187,7 +187,12 @@ class Create extends Base
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'latestDeploymentId' => $site->getAttribute('latestDeploymentId'),
|
||||
'latestDeploymentInternalId' => $site->getAttribute('latestDeploymentInternalId'),
|
||||
'latestDeploymentCreatedAt' => $site->getAttribute('latestDeploymentCreatedAt'),
|
||||
'latestDeploymentStatus' => $site->getAttribute('latestDeploymentStatus'),
|
||||
]));
|
||||
|
||||
$sitesDomain = $platform['sitesDomain'];
|
||||
$domain = ID::unique() . "." . $sitesDomain;
|
||||
|
||||
@@ -193,9 +193,12 @@ class Create extends Base
|
||||
$repository = $dbForPlatform->createDocument('repositories', $repository);
|
||||
$site->setAttribute('repositoryId', $repository->getId());
|
||||
$site->setAttribute('repositoryInternalId', $repository->getSequence());
|
||||
}
|
||||
|
||||
$site = $dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
$site = $dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'repositoryId' => $repository->getId(),
|
||||
'repositoryInternalId' => $repository->getSequence(),
|
||||
]));
|
||||
}
|
||||
|
||||
$queueForEvents->setParam('siteId', $site->getId());
|
||||
|
||||
|
||||
@@ -111,7 +111,10 @@ class Update extends Base
|
||||
->setAttribute('deploymentId', $deployment->getId())
|
||||
->setAttribute('deploymentInternalId', $deployment->getSequence());
|
||||
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), $rule));
|
||||
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'deploymentId' => $rule->getAttribute('deploymentId'),
|
||||
'deploymentInternalId' => $rule->getAttribute('deploymentInternalId'),
|
||||
])));
|
||||
}, $queries));
|
||||
|
||||
$queueForEvents
|
||||
|
||||
@@ -92,7 +92,9 @@ class Create extends Base
|
||||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site->setAttribute('live', false));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'live' => false,
|
||||
]));
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -76,7 +77,9 @@ class Delete extends Base
|
||||
|
||||
$dbForProject->deleteDocument('variables', $variable->getId());
|
||||
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site->setAttribute('live', false));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'live' => false,
|
||||
]));
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -93,12 +94,19 @@ class Update extends Base
|
||||
->setAttribute('search', implode(' ', [$variableId, $site->getId(), $key, 'site']));
|
||||
|
||||
try {
|
||||
$dbForProject->updateDocument('variables', $variable->getId(), $variable);
|
||||
$dbForProject->updateDocument('variables', $variable->getId(), new Document([
|
||||
'key' => $variable->getAttribute('key'),
|
||||
'value' => $variable->getAttribute('value'),
|
||||
'secret' => $variable->getAttribute('secret'),
|
||||
'search' => $variable->getAttribute('search'),
|
||||
]));
|
||||
} catch (DuplicateException $th) {
|
||||
throw new Exception(Exception::VARIABLE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('sites', $site->getId(), $site->setAttribute('live', false));
|
||||
$dbForProject->updateDocument('sites', $site->getId(), new Document([
|
||||
'live' => false,
|
||||
]));
|
||||
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
}
|
||||
|
||||
@@ -266,17 +266,22 @@ class Create extends Action
|
||||
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
|
||||
}
|
||||
} elseif ($membership->getAttribute('confirm') === false) {
|
||||
$membership->setAttribute('secret', $proofForToken->hash($secret));
|
||||
$membership->setAttribute('invited', DateTime::now());
|
||||
$secretHash = $proofForToken->hash($secret);
|
||||
$invitedTime = DateTime::now();
|
||||
|
||||
if ($isPrivilegedUser || $isAppUser) {
|
||||
$membership->setAttribute('joined', DateTime::now());
|
||||
$membership->setAttribute('confirm', true);
|
||||
$membership = $authorization->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), new Document([
|
||||
'secret' => $secretHash,
|
||||
'invited' => $invitedTime,
|
||||
'joined' => DateTime::now(),
|
||||
'confirm' => true
|
||||
])));
|
||||
} else {
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), new Document([
|
||||
'secret' => $secretHash,
|
||||
'invited' => $invitedTime
|
||||
]));
|
||||
}
|
||||
|
||||
$membership = ($isPrivilegedUser || $isAppUser) ?
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
|
||||
$dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
} else {
|
||||
throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED);
|
||||
}
|
||||
|
||||
@@ -131,7 +131,10 @@ class Delete extends Action
|
||||
if (!$membership->isEmpty()) {
|
||||
$team->setAttribute('userId', $membership->getAttribute('userId'));
|
||||
$team->setAttribute('userInternalId', $membership->getAttribute('userInternalId'));
|
||||
$dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
$dbForProject->updateDocument('teams', $team->getId(), new Document([
|
||||
'userId' => $membership->getAttribute('userId'),
|
||||
'userInternalId' => $membership->getAttribute('userInternalId'),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -189,7 +189,7 @@ class Update extends Action
|
||||
;
|
||||
}
|
||||
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), new Document(['joined' => $membership->getAttribute('joined'), 'confirm' => true]));
|
||||
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ class Update extends Action
|
||||
* Update the roles
|
||||
*/
|
||||
$membership->setAttribute('roles', $roles);
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), new Document(['roles' => $roles]));
|
||||
|
||||
/**
|
||||
* Replace membership on profile
|
||||
|
||||
@@ -10,6 +10,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
@@ -67,7 +68,7 @@ class Update extends Action
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('search', implode(' ', [$teamId, $name]));
|
||||
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team);
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), new Document(['name' => $name, 'search' => implode(' ', [$teamId, $name])]));
|
||||
|
||||
$queueForEvents->setParam('teamId', $team->getId());
|
||||
|
||||
|
||||
+1
-2
@@ -98,9 +98,8 @@ class Update extends Action
|
||||
}
|
||||
|
||||
$providerPullRequestIds = \array_unique(\array_merge($repository->getAttribute('providerPullRequestIds', []), [$providerPullRequestId]));
|
||||
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
|
||||
|
||||
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
|
||||
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), new Document(['providerPullRequestIds' => $providerPullRequestIds])));
|
||||
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
|
||||
@@ -152,7 +152,13 @@ class Get extends Action
|
||||
->setAttribute('personalRefreshToken', $refreshToken)
|
||||
->setAttribute('personalAccessToken', $accessToken)
|
||||
->setAttribute('personalAccessTokenExpiry', $accessTokenExpiry);
|
||||
$installation = $dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
$installation = $dbForPlatform->updateDocument('installations', $installation->getId(), new Document([
|
||||
'organization' => $installation->getAttribute('organization'),
|
||||
'personal' => $installation->getAttribute('personal'),
|
||||
'personalRefreshToken' => $installation->getAttribute('personalRefreshToken'),
|
||||
'personalAccessToken' => $installation->getAttribute('personalAccessToken'),
|
||||
'personalAccessTokenExpiry' => $installation->getAttribute('personalAccessTokenExpiry'),
|
||||
]));
|
||||
}
|
||||
} else {
|
||||
$error = 'Installation of the Appwrite GitHub App on organization accounts is restricted to organization owners. As a member of the organization, you do not have the necessary permissions to install this GitHub App. Please contact the organization owner to create the installation from the Appwrite console.';
|
||||
|
||||
@@ -326,7 +326,12 @@ trait Deployment
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
|
||||
$authorization->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), new Document([
|
||||
'latestDeploymentId' => $resource->getAttribute('latestDeploymentId'),
|
||||
'latestDeploymentInternalId' => $resource->getAttribute('latestDeploymentInternalId'),
|
||||
'latestDeploymentCreatedAt' => $resource->getAttribute('latestDeploymentCreatedAt'),
|
||||
'latestDeploymentStatus' => $resource->getAttribute('latestDeploymentStatus'),
|
||||
])));
|
||||
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
$projectId = $project->getId();
|
||||
|
||||
@@ -9,6 +9,7 @@ use Appwrite\Platform\Modules\VCS\Http\GitHub\Deployment;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -235,7 +236,7 @@ class Create extends Action
|
||||
if (\in_array($providerPullRequestId, $providerPullRequestIds)) {
|
||||
$providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]);
|
||||
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
|
||||
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
|
||||
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), new Document(['providerPullRequestIds' => $providerPullRequestIds])));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,11 @@ class Create extends Action
|
||||
->setAttribute('personalRefreshToken', $refreshToken)
|
||||
->setAttribute('personalAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
|
||||
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), new Document([
|
||||
'personalAccessToken' => $installation->getAttribute('personalAccessToken'),
|
||||
'personalRefreshToken' => $installation->getAttribute('personalRefreshToken'),
|
||||
'personalAccessTokenExpiry' => $installation->getAttribute('personalAccessTokenExpiry'),
|
||||
]));
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -159,9 +159,7 @@ class Interval extends Action
|
||||
}
|
||||
|
||||
foreach ($staleExecutions as $execution) {
|
||||
$execution->setAttribute('status', 'failed');
|
||||
$execution->setAttribute('errors', 'Execution timed out');
|
||||
$dbForProject->updateDocument('executions', $execution->getId(), $execution);
|
||||
$dbForProject->updateDocument('executions', $execution->getId(), new Document(['status' => 'failed', 'errors' => 'Execution timed out']));
|
||||
}
|
||||
|
||||
$processed++;
|
||||
|
||||
@@ -25,6 +25,7 @@ use Appwrite\SDK\Language\Swift;
|
||||
use Appwrite\SDK\Language\Web;
|
||||
use Appwrite\SDK\SDK;
|
||||
use Appwrite\Spec\Swagger2;
|
||||
use CzProject\GitPhp\Git;
|
||||
use Utopia\Agents\Adapters\OpenAI;
|
||||
use Utopia\Agents\DiffCheck\DiffCheck;
|
||||
use Utopia\Agents\DiffCheck\Options as DiffCheckOptions;
|
||||
@@ -41,29 +42,6 @@ use Utopia\Validator\WhiteList;
|
||||
|
||||
class SDKs extends Action
|
||||
{
|
||||
protected array $supportedSDKS = [
|
||||
'web',
|
||||
'cli',
|
||||
'php',
|
||||
'nodejs',
|
||||
'deno',
|
||||
'python',
|
||||
'ruby',
|
||||
'flutter',
|
||||
'react-native',
|
||||
'dart',
|
||||
'go',
|
||||
'swift',
|
||||
'apple',
|
||||
'dotnet',
|
||||
'android',
|
||||
'graphql',
|
||||
'rest',
|
||||
'markdown',
|
||||
'agent-skills',
|
||||
'cursor-plugin'
|
||||
];
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'sdks';
|
||||
@@ -74,6 +52,19 @@ class SDKs extends Action
|
||||
return Specs::getPlatforms();
|
||||
}
|
||||
|
||||
protected function getSdkConfigPath(): string
|
||||
{
|
||||
return __DIR__ . '/../../../../app/config/sdks.php';
|
||||
}
|
||||
|
||||
protected function getSupportedSDKs(): array
|
||||
{
|
||||
return \array_unique(\array_merge(...\array_values(\array_map(
|
||||
fn ($platform) => \array_column($platform['sdks'], 'key'),
|
||||
Config::getParam('sdks')
|
||||
))));
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
@@ -100,8 +91,9 @@ class SDKs extends Action
|
||||
if (! $sdks) {
|
||||
$selectedPlatform ??= Console::confirm('Choose Platform ("' . implode('", "', static::getPlatforms()) . '" or "*" for all):');
|
||||
$selectedSDK ??= \strtolower(Console::confirm('Choose SDK ("*" for all):'));
|
||||
if ($selectedSDK !== '*' && ! \in_array($selectedSDK, $this->supportedSDKS)) {
|
||||
throw new \Exception('Unknown SDK "' . $selectedSDK . '" given. Options are: ' . implode(', ', $this->supportedSDKS));
|
||||
$supportedSDKs = $this->getSupportedSDKs();
|
||||
if ($selectedSDK !== '*' && ! \in_array($selectedSDK, $supportedSDKs)) {
|
||||
throw new \Exception('Unknown SDK "' . $selectedSDK . '" given. Options are: ' . implode(', ', $supportedSDKs));
|
||||
}
|
||||
} else {
|
||||
$sdks = explode(',', $sdks);
|
||||
@@ -117,9 +109,6 @@ class SDKs extends Action
|
||||
|
||||
$prUrls = [];
|
||||
|
||||
if ($git) {
|
||||
$message ??= Console::confirm('Please enter your commit message:');
|
||||
}
|
||||
} elseif ($examplesOnly) {
|
||||
$git = false;
|
||||
$prUrls = [];
|
||||
@@ -337,7 +326,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
}
|
||||
|
||||
// Check if release already exists
|
||||
$checkReleaseCommand = 'gh release view "' . $releaseVersion . '" --repo "' . $repoName . '" --json url --jq ".url" 2>/dev/null';
|
||||
$checkReleaseCommand = 'gh release view ' . \escapeshellarg($releaseVersion) . ' --repo ' . \escapeshellarg($repoName) . ' --json url --jq ".url" 2>/dev/null';
|
||||
$existingReleaseUrl = trim(\shell_exec($checkReleaseCommand) ?? '');
|
||||
|
||||
if (! empty($existingReleaseUrl)) {
|
||||
@@ -368,7 +357,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
}
|
||||
|
||||
$previousVersion = '';
|
||||
$tagListCommand = 'gh release list --repo "' . $repoName . '" --limit 1 --json tagName --jq ".[0].tagName" 2>&1';
|
||||
$tagListCommand = 'gh release list --repo ' . \escapeshellarg($repoName) . ' --limit 1 --json tagName --jq ".[0].tagName" 2>&1';
|
||||
$previousVersion = trim(\shell_exec($tagListCommand) ?? '');
|
||||
|
||||
$formattedNotes = "## What's Changed\n\n";
|
||||
@@ -396,11 +385,11 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
$tempNotesFile = \tempnam(\sys_get_temp_dir(), 'release_notes_');
|
||||
\file_put_contents($tempNotesFile, $formattedNotes);
|
||||
|
||||
$releaseCommand = 'gh release create "' . $releaseVersion . '" \
|
||||
--repo "' . $repoName . '" \
|
||||
--title "' . $releaseTitle . '" \
|
||||
--notes-file "' . $tempNotesFile . '" \
|
||||
--target "' . $releaseTarget . '" \
|
||||
$releaseCommand = 'gh release create ' . \escapeshellarg($releaseVersion) . ' \
|
||||
--repo ' . \escapeshellarg($repoName) . ' \
|
||||
--title ' . \escapeshellarg($releaseTitle) . ' \
|
||||
--notes-file ' . \escapeshellarg($tempNotesFile) . ' \
|
||||
--target ' . \escapeshellarg($releaseTarget) . ' \
|
||||
2>&1';
|
||||
|
||||
$releaseOutput = [];
|
||||
@@ -491,7 +480,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
Console::info("Analyzing SDK changes with AI...");
|
||||
$aiResult = $this->generateVersionAndChangelog($language, $result);
|
||||
|
||||
if ($aiResult !== null) {
|
||||
if (!empty($aiResult['skip'])) {
|
||||
Console::warning("Skipping {$language['name']} SDK generation");
|
||||
continue;
|
||||
} elseif ($aiResult !== null) {
|
||||
$newVersion = $aiResult['version'];
|
||||
$newChangelog = $aiResult['changelog'];
|
||||
$aiChangelog = $newChangelog; // Store for PR description
|
||||
@@ -499,19 +491,17 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
// Update the version in the config
|
||||
$this->updateSdkVersion($key, $language['key'], $newVersion);
|
||||
|
||||
// Update the changelog file
|
||||
// Update the source changelog file
|
||||
$this->updateChangelogFile($language['changelog'], $newVersion, $newChangelog);
|
||||
|
||||
// Also update CHANGELOG.md in the generated SDK directory
|
||||
$sdkChangelogPath = $result . '/CHANGELOG.md';
|
||||
if (file_exists($sdkChangelogPath)) {
|
||||
$this->updateChangelogFile($sdkChangelogPath, $newVersion, $newChangelog);
|
||||
}
|
||||
// Re-read updated changelog so regeneration includes the new entry
|
||||
$updatedChangelog = \file_get_contents($language['changelog']);
|
||||
$sdk->setChangelog($updatedChangelog);
|
||||
|
||||
// Reload the language config with updated values
|
||||
$language['version'] = $newVersion;
|
||||
|
||||
// Regenerate SDK with new version
|
||||
// Regenerate SDK with new version and updated changelog
|
||||
$sdk->setVersion($newVersion);
|
||||
try {
|
||||
$sdk->generate($result);
|
||||
@@ -527,159 +517,26 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
$gitBranch = $language['gitBranch'];
|
||||
|
||||
$repoBranch = $language['repoBranch'] ?? 'main';
|
||||
if ($git && ! empty($gitUrl)) {
|
||||
Console::info("Preparing {$language['name']} SDK repository...");
|
||||
|
||||
\exec('rm -rf ' . $target . ' && \
|
||||
mkdir -p ' . $target . ' && \
|
||||
cd ' . $target . ' && \
|
||||
git init --quiet && \
|
||||
git config core.ignorecase false && \
|
||||
git config pull.rebase false && \
|
||||
git config advice.defaultBranchName false && \
|
||||
git remote add origin ' . $gitUrl . ' && \
|
||||
git fetch origin --quiet --no-tags --depth 1 ' . $repoBranch . ' 2>&1 | grep -v "^remote:" | grep -v "^From " | grep -v "^ \* " || true && \
|
||||
(git checkout -f ' . $repoBranch . ' 2>/dev/null || git checkout -b ' . $repoBranch . ') && \
|
||||
git pull origin ' . $repoBranch . ' --quiet --no-tags 2>&1 | grep -v "^From " | grep -v "^ \* " || true && \
|
||||
(git checkout -f ' . $gitBranch . ' 2>/dev/null || git checkout -b ' . $gitBranch . ') && \
|
||||
(git fetch origin ' . $gitBranch . ' --quiet --no-tags --depth 1 2>/dev/null || git push -u origin ' . $gitBranch . ' --quiet 2>&1 | grep -v "^remote:" || true) && \
|
||||
git reset --hard origin/' . $gitBranch . ' 2>/dev/null || true && \
|
||||
(if [ -d .github ]; then cp -r .github /tmp/.github-backup-$$ 2>/dev/null; fi) && \
|
||||
git rm -rf --cached . 2>/dev/null && \
|
||||
git clean -fdx -e .git -e .github 2>/dev/null && \
|
||||
cp -r ' . $result . '/. ' . $target . '/ && \
|
||||
(if [ -d /tmp/.github-backup-$$/.github ]; then cp -rn /tmp/.github-backup-$$/.github . 2>/dev/null && rm -rf /tmp/.github-backup-$$; fi) && \
|
||||
git add -A && \
|
||||
git commit -m "' . $message . '" --quiet && \
|
||||
git push -u origin ' . $gitBranch . ' --quiet 2>&1 | grep -E "^(To | |[0-9a-f]+\\.\\.[0-9a-f]+)" || true
|
||||
', $gitOutput, $gitReturnCode);
|
||||
|
||||
if ($gitReturnCode !== 0) {
|
||||
Console::warning("Git operations completed with warnings (exit code: {$gitReturnCode})");
|
||||
if ($git && !empty($gitUrl)) {
|
||||
// Generate commit message: use provided message, AI changelog, or fallback
|
||||
if (! empty($message)) {
|
||||
$commitMessage = $message;
|
||||
} elseif (! empty($aiChangelog) && $aiChangelog !== '* No user-facing SDK changes.') {
|
||||
$commitMessage = "feat: update {$language['name']} SDK to {$language['version']}\n\n{$aiChangelog}";
|
||||
} else {
|
||||
$commitMessage = "chore: update {$language['name']} SDK to {$language['version']}";
|
||||
}
|
||||
|
||||
Console::success("Pushed {$language['name']} SDK to {$gitUrl}");
|
||||
if ($git) {
|
||||
$prTitle = "feat: {$language['name']} SDK update for version {$language['version']}";
|
||||
$pushSuccess = $this->pushToGit($language, $target, $result, $gitUrl, $gitBranch, $repoBranch, $commitMessage);
|
||||
|
||||
// Build PR body with AI changelog if available
|
||||
$prBody = "This PR contains updates to the {$language['name']} SDK for version {$language['version']}.";
|
||||
if (!empty($aiChangelog) && $aiChangelog !== '* No user-facing SDK changes.') {
|
||||
$prBody .= "\n\n## Changes\n\n{$aiChangelog}";
|
||||
}
|
||||
$repoName = $language['gitUserName'] . '/' . $language['gitRepoName'];
|
||||
|
||||
Console::info("Creating pull request for {$language['name']} SDK...");
|
||||
|
||||
$prCommand = 'cd ' . $target . ' && \
|
||||
gh pr create \
|
||||
--repo "' . $repoName . '" \
|
||||
--title "' . $prTitle . '" \
|
||||
--body "' . $prBody . '" \
|
||||
--base "' . $repoBranch . '" \
|
||||
--head "' . $gitBranch . '" \
|
||||
2>&1';
|
||||
|
||||
$prOutput = [];
|
||||
$prReturnCode = 0;
|
||||
\exec($prCommand, $prOutput, $prReturnCode);
|
||||
|
||||
if ($prReturnCode === 0) {
|
||||
Console::success("Successfully created pull request for {$language['name']} SDK");
|
||||
if (! empty($prOutput)) {
|
||||
$prUrls[$language['name']] = end($prOutput);
|
||||
}
|
||||
} else {
|
||||
$errorMessage = implode("\n", $prOutput);
|
||||
if (strpos($errorMessage, 'already exists') !== false) {
|
||||
Console::warning("Pull request already exists for {$language['name']} SDK, updating title and body...");
|
||||
$prNumberCommand = 'cd ' . $target . ' && \
|
||||
gh pr list \
|
||||
--repo "' . $repoName . '" \
|
||||
--head "' . $gitBranch . '" \
|
||||
--json number \
|
||||
--jq ".[0].number" \
|
||||
2>&1';
|
||||
|
||||
$prNumberOutput = [];
|
||||
$prNumberReturnCode = 0;
|
||||
\exec($prNumberCommand, $prNumberOutput, $prNumberReturnCode);
|
||||
|
||||
if ($prNumberReturnCode === 0 && ! empty($prNumberOutput[0])) {
|
||||
$prNumber = trim($prNumberOutput[0]);
|
||||
|
||||
// Use API directly to update PR to avoid deprecated projectCards field
|
||||
$updateCommand = 'cd ' . $target . ' && \
|
||||
gh api \
|
||||
--method PATCH \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
/repos/' . $repoName . '/pulls/' . $prNumber . ' \
|
||||
-f title="' . $prTitle . '" \
|
||||
-f body="' . $prBody . '" \
|
||||
2>&1';
|
||||
|
||||
$updateOutput = [];
|
||||
$updateReturnCode = 0;
|
||||
\exec($updateCommand, $updateOutput, $updateReturnCode);
|
||||
|
||||
if ($updateReturnCode === 0) {
|
||||
Console::success("Successfully updated pull request for {$language['name']} SDK");
|
||||
|
||||
$prUrlCommand = 'cd ' . $target . ' && \
|
||||
gh pr list \
|
||||
--repo "' . $repoName . '" \
|
||||
--head "' . $gitBranch . '" \
|
||||
--json url \
|
||||
--jq ".[0].url" \
|
||||
2>&1';
|
||||
|
||||
$prUrlOutput = [];
|
||||
$prUrlReturnCode = 0;
|
||||
\exec($prUrlCommand, $prUrlOutput, $prUrlReturnCode);
|
||||
|
||||
if ($prUrlReturnCode === 0 && ! empty($prUrlOutput)) {
|
||||
$prUrls[$language['name']] = trim($prUrlOutput[0]);
|
||||
}
|
||||
} else {
|
||||
$updateErrorMessage = implode("\n", $updateOutput);
|
||||
Console::error("Failed to update pull request for {$language['name']} SDK: " . $updateErrorMessage);
|
||||
}
|
||||
} else {
|
||||
Console::error("Failed to get PR number for {$language['name']} SDK");
|
||||
}
|
||||
} else {
|
||||
Console::error("Failed to create pull request for {$language['name']} SDK: " . $errorMessage);
|
||||
}
|
||||
}
|
||||
if ($pushSuccess) {
|
||||
$this->createPullRequest($language, $target, $gitBranch, $repoBranch, $aiChangelog, $prUrls);
|
||||
}
|
||||
|
||||
\exec('chmod -R u+w ' . $target . ' && rm -rf ' . $target);
|
||||
Console::success("Remove temp directory '{$target}' for {$language['name']} SDK");
|
||||
$this->cleanupTarget($target, $language['name']);
|
||||
}
|
||||
|
||||
$docDirectories = $language['docDirectories'] ?? [''];
|
||||
|
||||
if ($version === 'latest') {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($docDirectories as $languageTitle => $path) {
|
||||
$languagePath = strtolower($languageTitle !== 0 ? '/' . $languageTitle : '');
|
||||
$examplesSource = $result . '/docs/examples' . $languagePath;
|
||||
|
||||
if (! \is_dir($examplesSource)) {
|
||||
Console::warning("No code examples found for {$language['name']} SDK at: {$examplesSource}. Skipping copy.");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
\exec(
|
||||
'mkdir -p ' . $resultExamples . $languagePath . ' && \
|
||||
cp -r ' . $examplesSource . ' ' . $resultExamples
|
||||
);
|
||||
Console::success("Copied code examples for {$language['name']} SDK to: {$resultExamples}");
|
||||
}
|
||||
$this->copyExamples($language, $version, $result, $resultExamples);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,6 +550,176 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
}
|
||||
}
|
||||
|
||||
private function pushToGit(array $language, string $target, string $result, string $gitUrl, string $gitBranch, string $repoBranch, string $commitMessage): bool
|
||||
{
|
||||
Console::info("Preparing {$language['name']} SDK repository...");
|
||||
|
||||
try {
|
||||
// Init fresh repo
|
||||
\exec('rm -rf ' . \escapeshellarg($target));
|
||||
\mkdir($target, 0755, true);
|
||||
|
||||
$gitClient = new Git();
|
||||
$repo = $gitClient->init($target);
|
||||
|
||||
$repo->execute('config', 'core.ignorecase', 'false');
|
||||
$repo->execute('config', 'pull.rebase', 'false');
|
||||
$repo->execute('config', 'advice.defaultBranchName', 'false');
|
||||
$repo->addRemote('origin', $gitUrl);
|
||||
|
||||
// Fetch and checkout base branch (or create if new repo)
|
||||
try {
|
||||
$repo->execute('fetch', 'origin', '--quiet', '--no-tags', '--depth', '1', $repoBranch);
|
||||
try {
|
||||
$repo->execute('checkout', '-f', $repoBranch);
|
||||
} catch (\Throwable) {
|
||||
$repo->execute('checkout', '-b', $repoBranch);
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
$repo->execute('checkout', '-b', $repoBranch);
|
||||
}
|
||||
|
||||
try {
|
||||
$repo->execute('pull', 'origin', $repoBranch, '--quiet', '--no-tags');
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
// Checkout dev branch (or create if it doesn't exist)
|
||||
try {
|
||||
$repo->execute('checkout', '-f', $gitBranch);
|
||||
} catch (\Throwable) {
|
||||
$repo->execute('checkout', '-b', $gitBranch);
|
||||
}
|
||||
|
||||
// Fetch dev branch, or push to create it on remote
|
||||
try {
|
||||
$repo->execute('fetch', 'origin', $gitBranch, '--quiet', '--no-tags', '--depth', '1');
|
||||
} catch (\Throwable) {
|
||||
try {
|
||||
$repo->execute('push', '-u', 'origin', $gitBranch, '--quiet');
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
// Sync with remote dev branch
|
||||
try {
|
||||
$repo->execute('reset', '--hard', "origin/{$gitBranch}");
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
// Backup .github before cleaning working tree
|
||||
$githubDir = $target . '/.github';
|
||||
$githubBackup = \sys_get_temp_dir() . '/.github-backup-' . \getmypid();
|
||||
$hasGithubDir = \is_dir($githubDir);
|
||||
if ($hasGithubDir) {
|
||||
\exec('cp -r ' . \escapeshellarg($githubDir) . ' ' . \escapeshellarg($githubBackup));
|
||||
}
|
||||
|
||||
// Clean working tree
|
||||
try {
|
||||
$repo->execute('rm', '-rf', '--cached', '.');
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
try {
|
||||
$repo->execute('clean', '-fdx', '-e', '.git', '-e', '.github');
|
||||
} catch (\Throwable) {
|
||||
}
|
||||
|
||||
// Copy generated SDK and restore .github
|
||||
\exec('cp -r ' . \escapeshellarg($result . '/.') . ' ' . \escapeshellarg($target . '/'));
|
||||
|
||||
if ($hasGithubDir && \is_dir($githubBackup)) {
|
||||
\exec('cp -rn ' . \escapeshellarg($githubBackup . '/.github') . ' ' . \escapeshellarg($target . '/') . ' 2>/dev/null');
|
||||
\exec('rm -rf ' . \escapeshellarg($githubBackup));
|
||||
}
|
||||
|
||||
// Stage, commit, push
|
||||
$repo->addAllChanges();
|
||||
$repo->commit($commitMessage);
|
||||
$repo->execute('push', '-u', 'origin', $gitBranch, '--quiet');
|
||||
} catch (\Throwable $e) {
|
||||
Console::warning("Git operations failed for {$language['name']} SDK: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
Console::success("Pushed {$language['name']} SDK to {$gitUrl}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createPullRequest(array $language, string $target, string $gitBranch, string $repoBranch, string $aiChangelog, array &$prUrls): void
|
||||
{
|
||||
$prTitle = "feat: {$language['name']} SDK update for version {$language['version']}";
|
||||
$prBody = "This PR contains updates to the {$language['name']} SDK for version {$language['version']}.";
|
||||
if (!empty($aiChangelog) && $aiChangelog !== '* No user-facing SDK changes.') {
|
||||
$prBody .= "\n\n## Changes\n\n{$aiChangelog}";
|
||||
}
|
||||
$repoName = $language['gitUserName'] . '/' . $language['gitRepoName'];
|
||||
|
||||
Console::info("Creating pull request for {$language['name']} SDK...");
|
||||
|
||||
$prCommand = 'cd ' . $target . ' && \
|
||||
gh pr create \
|
||||
--repo ' . \escapeshellarg($repoName) . ' \
|
||||
--title ' . \escapeshellarg($prTitle) . ' \
|
||||
--body ' . \escapeshellarg($prBody) . ' \
|
||||
--base ' . \escapeshellarg($repoBranch) . ' \
|
||||
--head ' . \escapeshellarg($gitBranch) . ' \
|
||||
2>&1';
|
||||
|
||||
$prOutput = [];
|
||||
$prReturnCode = 0;
|
||||
\exec($prCommand, $prOutput, $prReturnCode);
|
||||
|
||||
if ($prReturnCode === 0) {
|
||||
Console::success("Successfully created pull request for {$language['name']} SDK");
|
||||
foreach ($prOutput as $line) {
|
||||
if (\str_starts_with(trim($line), 'https://')) {
|
||||
$prUrls[$language['name']] = trim($line);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$errorMessage = implode("\n", $prOutput);
|
||||
if (strpos($errorMessage, 'already exists') === false) {
|
||||
Console::error("Failed to create pull request for {$language['name']} SDK: " . $errorMessage);
|
||||
} else {
|
||||
$this->updateExistingPr($target, $repoName, $gitBranch, $prTitle, $prBody, $language['name'], $prUrls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function cleanupTarget(string $target, string $languageName): void
|
||||
{
|
||||
\exec('chmod -R u+w ' . $target . ' && rm -rf ' . $target);
|
||||
Console::success("Remove temp directory '{$target}' for {$languageName} SDK");
|
||||
}
|
||||
|
||||
private function copyExamples(array $language, string $version, string $result, string $resultExamples): void
|
||||
{
|
||||
$docDirectories = $language['docDirectories'] ?? [''];
|
||||
|
||||
if ($version === 'latest') {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($docDirectories as $languageTitle => $path) {
|
||||
$languagePath = strtolower($languageTitle !== 0 ? '/' . $languageTitle : '');
|
||||
$examplesSource = $result . '/docs/examples' . $languagePath;
|
||||
|
||||
if (! \is_dir($examplesSource)) {
|
||||
Console::warning("No code examples found for {$language['name']} SDK at: {$examplesSource}. Skipping copy.");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
\exec(
|
||||
'mkdir -p ' . $resultExamples . $languagePath . ' && \
|
||||
cp -r ' . $examplesSource . ' ' . $resultExamples
|
||||
);
|
||||
Console::success("Copied code examples for {$language['name']} SDK to: {$resultExamples}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract release notes from changelog for a specific version
|
||||
*/
|
||||
@@ -780,20 +807,25 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
required: $object->getNames()
|
||||
);
|
||||
|
||||
$isBeta = !empty($language['beta']);
|
||||
$betaNote = $isBeta
|
||||
? "\n Note: This SDK is in beta (version < 1.0.0). Do NOT bump to 1.0.0. Use `minor` for both breaking changes and new features, `patch` for bug fixes only."
|
||||
: '';
|
||||
|
||||
$prompt = <<<PROMPT
|
||||
You are a technical writer generating a changelog for the {$language['name']} SDK release.
|
||||
|
||||
|
||||
Analyze the git diff below and return a JSON response with the version bump type, new version number, and changelog.
|
||||
|
||||
|
||||
## Versioning
|
||||
|
||||
|
||||
Current version: {$language['version']}
|
||||
|
||||
|
||||
Determine the semantic version bump:
|
||||
- `major`: Breaking changes (removed/renamed public APIs, changed method signatures, dropped support)
|
||||
- `minor`: New features that are backward-compatible (new methods, new optional parameters, new classes)
|
||||
- `patch`: Bug fixes, documentation updates, refactors with no API surface change
|
||||
|
||||
{$betaNote}
|
||||
When multiple change types are present, use the highest severity bump.
|
||||
|
||||
## Changelog guidelines
|
||||
@@ -810,6 +842,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
- Only include changes visible to SDK users (public API, behavior, docs, examples, CLI)
|
||||
- Ignore: CI/CD pipelines (.github/), internal tooling, code formatting, test infrastructure
|
||||
- Consolidate related changes into one entry (e.g., "Added `timeout`, `retries`, and `baseUrl` options" not three separate lines)
|
||||
- Wrap all method names, parameter names, class names, and code identifiers in backticks (e.g., `listDocuments`, `ttl`)
|
||||
- If the diff contains zero user-facing changes, return a single entry: "No user-facing SDK changes"
|
||||
- Do not speculate — only document what the diff explicitly shows
|
||||
|
||||
@@ -856,7 +889,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
if (empty(trim($responseContent))) {
|
||||
Console::warning('AI returned empty response');
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -866,7 +898,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
Console::warning('Failed to parse AI response as JSON: ' . json_last_error_msg());
|
||||
Console::log('Raw response:');
|
||||
Console::log($responseContent);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -875,6 +906,12 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
return null;
|
||||
}
|
||||
|
||||
// Guard: beta SDKs must not be bumped to >= 1.0.0
|
||||
if ($isBeta && ($parsed['versionBump'] === 'major' || \version_compare($parsed['version'], '1.0.0', '>='))) {
|
||||
Console::warning("Beta SDK {$language['name']} cannot have a major bump or version >= 1.0.0 (AI suggested {$parsed['version']}), skipping");
|
||||
return ['skip' => true];
|
||||
}
|
||||
|
||||
Console::success("✓ Analysis complete");
|
||||
Console::log(" Version: {$language['version']} → {$parsed['version']} ({$parsed['versionBump']} bump)");
|
||||
Console::log(" Changelog:");
|
||||
@@ -891,21 +928,10 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
];
|
||||
} catch (\Throwable $e) {
|
||||
Console::error('Error generating version and changelog: ' . $e->getMessage());
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SDK config file path
|
||||
*
|
||||
* @return string Path to the SDK config file
|
||||
*/
|
||||
protected function getSdkConfigPath(): string
|
||||
{
|
||||
return __DIR__ . '/../../../../app/config/sdks.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Update SDK version in the config file
|
||||
*
|
||||
@@ -920,7 +946,6 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
if (! file_exists($configPath)) {
|
||||
Console::error("Config file not found: {$configPath}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -936,11 +961,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
if (file_put_contents($configPath, $newContent) !== false) {
|
||||
Console::success("Updated {$sdkKey} version from {$oldVersion} to {$newVersion} in config");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Console::error('Failed to write config file');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -956,11 +979,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
if (file_put_contents($configPath, $newContent) !== false) {
|
||||
Console::success("Updated {$sdkKey} version from {$oldVersion} to {$newVersion} in config");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Console::error('Failed to write config file');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1019,12 +1040,71 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
|
||||
if (file_put_contents($changelogPath, $newContent) !== false) {
|
||||
Console::success("Updated changelog at {$changelogPath} with version {$version}");
|
||||
|
||||
return true;
|
||||
} else {
|
||||
Console::error('Failed to write changelog file');
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function updateExistingPr(string $target, string $repoName, string $gitBranch, string $prTitle, string $prBody, string $sdkName, array &$prUrls): void
|
||||
{
|
||||
Console::warning("Pull request already exists for {$sdkName} SDK, updating title and body...");
|
||||
|
||||
$prNumberCommand = 'cd ' . $target . ' && \
|
||||
gh pr list \
|
||||
--repo ' . \escapeshellarg($repoName) . ' \
|
||||
--head ' . \escapeshellarg($gitBranch) . ' \
|
||||
--json number \
|
||||
--jq ".[0].number" \
|
||||
2>&1';
|
||||
|
||||
$prNumberOutput = [];
|
||||
$prNumberReturnCode = 0;
|
||||
\exec($prNumberCommand, $prNumberOutput, $prNumberReturnCode);
|
||||
|
||||
if ($prNumberReturnCode !== 0 || empty($prNumberOutput[0])) {
|
||||
Console::error("Failed to get PR number for {$sdkName} SDK");
|
||||
return;
|
||||
}
|
||||
|
||||
$prNumber = trim($prNumberOutput[0]);
|
||||
$apiPath = "/repos/{$repoName}/pulls/{$prNumber}";
|
||||
$updateCommand = 'cd ' . $target . ' && \
|
||||
gh api \
|
||||
--method PATCH \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
' . \escapeshellarg($apiPath) . ' \
|
||||
-f title=' . \escapeshellarg($prTitle) . ' \
|
||||
-f body=' . \escapeshellarg($prBody) . ' \
|
||||
2>&1';
|
||||
|
||||
$updateOutput = [];
|
||||
$updateReturnCode = 0;
|
||||
\exec($updateCommand, $updateOutput, $updateReturnCode);
|
||||
|
||||
if ($updateReturnCode !== 0) {
|
||||
Console::error("Failed to update pull request for {$sdkName} SDK: " . implode("\n", $updateOutput));
|
||||
return;
|
||||
}
|
||||
|
||||
Console::success("Successfully updated pull request for {$sdkName} SDK");
|
||||
|
||||
$prUrlCommand = 'cd ' . $target . ' && \
|
||||
gh pr list \
|
||||
--repo ' . \escapeshellarg($repoName) . ' \
|
||||
--head ' . \escapeshellarg($gitBranch) . ' \
|
||||
--json url \
|
||||
--jq ".[0].url" \
|
||||
2>&1';
|
||||
|
||||
$prUrlOutput = [];
|
||||
$prUrlReturnCode = 0;
|
||||
\exec($prUrlCommand, $prUrlOutput, $prUrlReturnCode);
|
||||
|
||||
if ($prUrlReturnCode === 0 && ! empty($prUrlOutput)) {
|
||||
$prUrls[$sdkName] = trim($prUrlOutput[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,8 +59,11 @@ abstract class ScheduleBase extends Action
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
$accessedAt = $project->getAttribute('accessedAt', 0);
|
||||
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
|
||||
$project->setAttribute('accessedAt', DateTime::now());
|
||||
$dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
$now = DateTime::now();
|
||||
$dbForPlatform->updateDocument('projects', $project->getId(), new Document([
|
||||
'accessedAt' => $now
|
||||
]));
|
||||
$project->setAttribute('accessedAt', $now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +423,11 @@ class Certificates extends Action
|
||||
Func $queueForFunctions,
|
||||
Realtime $queueForRealtime
|
||||
): void {
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), $rule);
|
||||
$rule = $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
|
||||
'status' => $rule->getAttribute('status'),
|
||||
'certificateId' => $rule->getAttribute('certificateId'),
|
||||
'logs' => $rule->getAttribute('logs'),
|
||||
]));
|
||||
$projectId = $rule->getAttribute('projectId');
|
||||
|
||||
// Skip events for console project (triggered by auto-ssl generation for 1 click setups)
|
||||
|
||||
@@ -365,7 +365,13 @@ class Messaging extends Action
|
||||
$message->setAttribute('deliveredTotal', $deliveredTotal);
|
||||
$message->setAttribute('deliveredAt', DateTime::now());
|
||||
|
||||
$dbForProject->updateDocument('messages', $message->getId(), $message);
|
||||
$dbForProject->updateDocument('messages', $message->getId(), new Document([
|
||||
'deliveryErrors' => $message->getAttribute('deliveryErrors'),
|
||||
'status' => $message->getAttribute('status'),
|
||||
'search' => $message->getAttribute('search'),
|
||||
'deliveredTotal' => $message->getAttribute('deliveredTotal'),
|
||||
'deliveredAt' => $message->getAttribute('deliveredAt'),
|
||||
]));
|
||||
|
||||
// Delete any attachments that were downloaded to local storage
|
||||
if ($provider->getAttribute('type') === MESSAGE_TYPE_EMAIL) {
|
||||
|
||||
@@ -571,11 +571,10 @@ class Migrations extends Action
|
||||
} finally {
|
||||
$message = "Export file size {$sizeMB}MB exceeds your plan limit.";
|
||||
|
||||
$this->dbForProject->updateDocument('migrations', $migration->getId(), $migration->setAttribute(
|
||||
'errors',
|
||||
json_encode(['code' => 0, 'message' => $message]),
|
||||
Document::SET_TYPE_APPEND,
|
||||
));
|
||||
$errors = $migration->getAttribute('errors', []);
|
||||
$errors[] = json_encode(['code' => 0, 'message' => $message]);
|
||||
$migration->setAttribute('errors', $errors);
|
||||
$migration = $this->updateMigrationDocument($migration, $project, $queueForRealtime);
|
||||
|
||||
$this->sendCSVEmail(
|
||||
success: false,
|
||||
|
||||
@@ -175,7 +175,7 @@ class StatsResources extends Action
|
||||
try {
|
||||
$this->countImageTransformations($dbForProject, $dbForLogs, $region);
|
||||
} catch (Throwable $th) {
|
||||
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_buckets_{$project->getId()}"]);
|
||||
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_image_transformations_{$project->getId()}"]);
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -227,9 +227,15 @@ class StatsResources extends Action
|
||||
$totalImageTransformations = 0;
|
||||
$last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00');
|
||||
$this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $last30Days, $region, &$totalImageTransformations) {
|
||||
$imageTransformations = $dbForProject->count('bucket_' . $bucket->getSequence(), [
|
||||
Query::greaterThanEqual('transformedAt', $last30Days),
|
||||
]);
|
||||
try {
|
||||
$imageTransformations = $dbForProject->count('bucket_' . $bucket->getSequence(), [
|
||||
Query::greaterThanEqual('transformedAt', $last30Days),
|
||||
]);
|
||||
} catch (Throwable $th) {
|
||||
call_user_func_array($this->logError, [$th, "StatsResources", "count_for_image_transformations_bucket_{$bucket->getSequence()}"]);
|
||||
return;
|
||||
}
|
||||
|
||||
$metric = str_replace('{bucketInternalId}', $bucket->getSequence(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED);
|
||||
$this->createStatsDocuments($region, $metric, $imageTransformations);
|
||||
$totalImageTransformations += $imageTransformations;
|
||||
|
||||
@@ -168,12 +168,15 @@ class Webhooks extends Action
|
||||
|
||||
$webhook->setAttribute('logs', $logs);
|
||||
|
||||
$updatePayload = ['logs' => $logs];
|
||||
|
||||
if ($attempts >= \intval(System::getEnv('_APP_WEBHOOK_MAX_FAILED_ATTEMPTS', '10'))) {
|
||||
$webhook->setAttribute('enabled', false);
|
||||
$updatePayload['enabled'] = false;
|
||||
$this->sendEmailAlert($attempts, $statusCode, $webhook, $project, $dbForPlatform, $queueForMails, $plan);
|
||||
}
|
||||
|
||||
$dbForPlatform->updateDocument('webhooks', $webhook->getId(), $webhook);
|
||||
$dbForPlatform->updateDocument('webhooks', $webhook->getId(), new Document($updatePayload));
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
|
||||
$this->errors[] = $logs;
|
||||
@@ -184,8 +187,9 @@ class Webhooks extends Action
|
||||
|
||||
|
||||
} else {
|
||||
$webhook->setAttribute('attempts', 0); // Reset attempts on success
|
||||
$dbForPlatform->updateDocument('webhooks', $webhook->getId(), $webhook);
|
||||
$dbForPlatform->updateDocument('webhooks', $webhook->getId(), new Document([
|
||||
'attempts' => 0,
|
||||
]));
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
$queueForStatsUsage
|
||||
->addMetric(METRIC_WEBHOOKS_SENT, 1)
|
||||
|
||||
@@ -128,7 +128,7 @@ class HooksTest extends Scope
|
||||
'cookie' => $cookie,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertEquals(403, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for api controllers
|
||||
@@ -140,7 +140,7 @@ class HooksTest extends Scope
|
||||
'cookie' => $cookie,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertEquals(403, $response['headers']['status-code']);
|
||||
$this->assertEquals(Exception::USER_BLOCKED, $response['body']['type']);
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,67 +102,79 @@ trait ProjectCustom
|
||||
$this->assertEquals(201, $project['headers']['status-code'], 'Project creation failed with status: ' . $project['headers']['status-code']);
|
||||
$this->assertNotEmpty($project['body']);
|
||||
|
||||
$key = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/keys', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'keyId' => ID::unique(),
|
||||
'name' => 'Demo Project Key',
|
||||
'scopes' => [
|
||||
'users.read',
|
||||
'users.write',
|
||||
'teams.read',
|
||||
'teams.write',
|
||||
'databases.read',
|
||||
'databases.write',
|
||||
'collections.read',
|
||||
'collections.write',
|
||||
'tables.read',
|
||||
'tables.write',
|
||||
'documents.read',
|
||||
'documents.write',
|
||||
'rows.read',
|
||||
'rows.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'buckets.read',
|
||||
'buckets.write',
|
||||
'sites.read',
|
||||
'sites.write',
|
||||
'functions.read',
|
||||
'functions.write',
|
||||
'sites.read',
|
||||
'sites.write',
|
||||
'execution.read',
|
||||
'execution.write',
|
||||
'log.read',
|
||||
'log.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'health.read',
|
||||
'rules.read',
|
||||
'rules.write',
|
||||
'sessions.write',
|
||||
'targets.read',
|
||||
'targets.write',
|
||||
'providers.read',
|
||||
'providers.write',
|
||||
'messages.read',
|
||||
'messages.write',
|
||||
'topics.write',
|
||||
'topics.read',
|
||||
'subscribers.write',
|
||||
'subscribers.read',
|
||||
'migrations.write',
|
||||
'migrations.read',
|
||||
'tokens.read',
|
||||
'tokens.write',
|
||||
],
|
||||
]);
|
||||
$key = null;
|
||||
for ($i = 0; $i < $maxRetries; $i++) {
|
||||
$key = $this->client->call(Client::METHOD_POST, '/projects/' . $project['body']['$id'] . '/keys', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'keyId' => ID::unique(),
|
||||
'name' => 'Demo Project Key',
|
||||
'scopes' => [
|
||||
'users.read',
|
||||
'users.write',
|
||||
'teams.read',
|
||||
'teams.write',
|
||||
'databases.read',
|
||||
'databases.write',
|
||||
'collections.read',
|
||||
'collections.write',
|
||||
'tables.read',
|
||||
'tables.write',
|
||||
'documents.read',
|
||||
'documents.write',
|
||||
'rows.read',
|
||||
'rows.write',
|
||||
'files.read',
|
||||
'files.write',
|
||||
'buckets.read',
|
||||
'buckets.write',
|
||||
'sites.read',
|
||||
'sites.write',
|
||||
'functions.read',
|
||||
'functions.write',
|
||||
'sites.read',
|
||||
'sites.write',
|
||||
'execution.read',
|
||||
'execution.write',
|
||||
'log.read',
|
||||
'log.write',
|
||||
'locale.read',
|
||||
'avatars.read',
|
||||
'health.read',
|
||||
'rules.read',
|
||||
'rules.write',
|
||||
'sessions.write',
|
||||
'targets.read',
|
||||
'targets.write',
|
||||
'providers.read',
|
||||
'providers.write',
|
||||
'messages.read',
|
||||
'messages.write',
|
||||
'topics.write',
|
||||
'topics.read',
|
||||
'subscribers.write',
|
||||
'subscribers.read',
|
||||
'migrations.write',
|
||||
'migrations.read',
|
||||
'tokens.read',
|
||||
'tokens.write',
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $key['headers']['status-code']);
|
||||
if ($key['headers']['status-code'] === 201) {
|
||||
break;
|
||||
}
|
||||
|
||||
if ($key['headers']['status-code'] === 401 && $i < $maxRetries - 1) {
|
||||
\usleep(500000);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$this->assertEquals(201, $key['headers']['status-code'], 'Key creation failed with status: ' . $key['headers']['status-code']);
|
||||
$this->assertNotEmpty($key['body']);
|
||||
$this->assertNotEmpty($key['body']['secret']);
|
||||
|
||||
|
||||
+72
-37
@@ -59,12 +59,21 @@ abstract class Scope extends TestCase
|
||||
|
||||
$root = $this->getRoot();
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/variables', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
'cookie' => 'a_session_console=' . $root['session'],
|
||||
]);
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/variables', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
'cookie' => 'a_session_console=' . $root['session'],
|
||||
]);
|
||||
|
||||
if ($response['headers']['status-code'] === 200 && !empty($response['body'])) {
|
||||
self::$consoleVariables = $response['body'];
|
||||
return self::$consoleVariables;
|
||||
}
|
||||
|
||||
\usleep(500000);
|
||||
}
|
||||
|
||||
self::$consoleVariables = $response['body'] ?? [];
|
||||
|
||||
@@ -140,7 +149,7 @@ abstract class Scope extends TestCase
|
||||
*/
|
||||
protected function getMaxIndexLength(): int
|
||||
{
|
||||
return $this->getConsoleVariables()['maxIndexLength'] ?? 768;
|
||||
return $this->getConsoleVariables()['maxIndexLength'] ?? 767;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,41 +441,67 @@ abstract class Scope extends TestCase
|
||||
return self::$root;
|
||||
}
|
||||
|
||||
// Use more entropy to avoid collisions in parallel test execution
|
||||
$email = uniqid('', true) . getmypid() . bin2hex(random_bytes(4)) . '@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name';
|
||||
$maxRetries = 5;
|
||||
|
||||
$root = $this->client->call(Client::METHOD_POST, '/account', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
]);
|
||||
for ($attempt = 0; $attempt < $maxRetries; $attempt++) {
|
||||
// Use more entropy to avoid collisions in parallel test execution
|
||||
$email = uniqid('', true) . getmypid() . bin2hex(random_bytes(4)) . '@localhost.test';
|
||||
$password = 'password';
|
||||
$name = 'User Name';
|
||||
|
||||
$this->assertEquals(201, $root['headers']['status-code']);
|
||||
$root = $this->client->call(Client::METHOD_POST, '/account', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'userId' => ID::unique(),
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
'name' => $name,
|
||||
]);
|
||||
|
||||
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
if ($root['headers']['status-code'] !== 201) {
|
||||
\usleep(500000);
|
||||
continue;
|
||||
}
|
||||
|
||||
self::$root = [
|
||||
'$id' => ID::custom($root['body']['$id']),
|
||||
'name' => $root['body']['name'],
|
||||
'email' => $root['body']['email'],
|
||||
'session' => $session['cookies']['a_session_console'],
|
||||
];
|
||||
$session = $this->client->call(Client::METHOD_POST, '/account/sessions/email', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'email' => $email,
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
return self::$root;
|
||||
if (empty($session['cookies']['a_session_console'])) {
|
||||
\usleep(500000);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Verify session is valid before returning
|
||||
$verify = $this->client->call(Client::METHOD_GET, '/account', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $session['cookies']['a_session_console'],
|
||||
'x-appwrite-project' => 'console',
|
||||
]);
|
||||
|
||||
if ($verify['headers']['status-code'] === 200) {
|
||||
self::$root = [
|
||||
'$id' => ID::custom($root['body']['$id']),
|
||||
'name' => $root['body']['name'],
|
||||
'email' => $root['body']['email'],
|
||||
'session' => $session['cookies']['a_session_console'],
|
||||
];
|
||||
|
||||
return self::$root;
|
||||
}
|
||||
|
||||
\usleep(500000);
|
||||
}
|
||||
|
||||
$this->fail('Failed to create and verify root session after ' . $maxRetries . ' attempts');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2360,7 +2360,7 @@ class AccountCustomClientTest extends Scope
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertEquals(403, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
@@ -2371,7 +2371,7 @@ class AccountCustomClientTest extends Scope
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertEquals(403, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
|
||||
@@ -2440,7 +2440,7 @@ class AccountCustomClientTest extends Scope
|
||||
'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session,
|
||||
]));
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertEquals(403, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
@@ -2451,7 +2451,7 @@ class AccountCustomClientTest extends Scope
|
||||
'password' => $password,
|
||||
]);
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertEquals(403, $response['headers']['status-code']);
|
||||
}
|
||||
|
||||
public function testCreateJWT(): void
|
||||
|
||||
@@ -1270,8 +1270,7 @@ trait DatabasesBase
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $attribute['headers']['status-code']);
|
||||
$maxLength = $this->getMaxIndexLength();
|
||||
$this->assertStringContainsString('Index length is longer than the maximum: '.$maxLength, $attribute['body']['message']);
|
||||
$this->assertStringContainsString('Index length is longer than the maximum:', $attribute['body']['message']);
|
||||
}
|
||||
|
||||
public function testUpdateAttributeEnum(): void
|
||||
|
||||
@@ -2771,7 +2771,6 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertLessThanOrEqual(APP_FUNCTION_LOG_LENGTH_LIMIT, strlen($logs));
|
||||
$this->assertStringStartsWith('[WARNING] Logs truncated', $logs);
|
||||
|
||||
$this->assertStringNotContainsString('z', $logs);
|
||||
$this->assertStringContainsString('a', $logs);
|
||||
|
||||
// Verify errors are truncated and warning message is present at the beginning
|
||||
@@ -2779,7 +2778,6 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertLessThanOrEqual(APP_FUNCTION_ERROR_LENGTH_LIMIT, strlen($errors));
|
||||
$this->assertStringStartsWith('[WARNING] Errors truncated', $errors);
|
||||
|
||||
$this->assertStringNotContainsString('z', $errors);
|
||||
$this->assertStringContainsString('a', $errors);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
|
||||
@@ -26,26 +26,39 @@ trait ProjectsBase
|
||||
return self::$cachedProjectData;
|
||||
}
|
||||
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
'teamId' => $team['body']['$id'],
|
||||
'region' => System::getEnv('_APP_REGION', 'default')
|
||||
]);
|
||||
$teamId = ID::unique();
|
||||
$team = null;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => $teamId,
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
if (\in_array($team['headers']['status-code'], [201, 409])) {
|
||||
break;
|
||||
}
|
||||
\usleep(500000);
|
||||
}
|
||||
$this->assertContains($team['headers']['status-code'], [201, 409]);
|
||||
|
||||
$project = null;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
'teamId' => $team['body']['$id'] ?? $teamId,
|
||||
'region' => System::getEnv('_APP_REGION', 'default')
|
||||
]);
|
||||
if ($project['headers']['status-code'] === 201) {
|
||||
break;
|
||||
}
|
||||
\usleep(500000);
|
||||
}
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
|
||||
self::$cachedProjectData = [
|
||||
@@ -396,27 +409,42 @@ trait ProjectsBase
|
||||
protected function setupProject(mixed $params, ?string $teamId = null, bool $newTeam = true): string
|
||||
{
|
||||
if ($newTeam) {
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
$generatedTeamId = $teamId ?? ID::unique();
|
||||
$team = null;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => $generatedTeamId,
|
||||
'name' => 'Project Test',
|
||||
]);
|
||||
if (\in_array($team['headers']['status-code'], [201, 409])) {
|
||||
break;
|
||||
}
|
||||
\usleep(500000);
|
||||
}
|
||||
|
||||
$this->assertContains($team['headers']['status-code'], [201, 409], 'Setup team failed with status code: ' . $team['headers']['status-code'] . ' and response: ' . json_encode($team['body'], JSON_PRETTY_PRINT));
|
||||
|
||||
$teamId = $team['body']['$id'] ?? $generatedTeamId;
|
||||
}
|
||||
|
||||
$project = null;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => $teamId ?? ID::unique(),
|
||||
'name' => 'Project Test',
|
||||
...$params,
|
||||
'teamId' => $teamId,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $team['headers']['status-code'], 'Setup team failed with status code: ' . $team['headers']['status-code'] . ' and response: ' . json_encode($team['body'], JSON_PRETTY_PRINT));
|
||||
|
||||
$teamId = $team['body']['$id'];
|
||||
if ($project['headers']['status-code'] === 201) {
|
||||
break;
|
||||
}
|
||||
\usleep(500000);
|
||||
}
|
||||
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
...$params,
|
||||
'teamId' => $teamId,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $project['headers']['status-code'], 'Setup project failed with status code: ' . $project['headers']['status-code'] . ' and response: ' . json_encode($project['body'], JSON_PRETTY_PRINT));
|
||||
|
||||
return $project['body']['$id'];
|
||||
|
||||
@@ -951,26 +951,22 @@ class ProjectsConsoleClientTest extends Scope
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$emails = $this->getLastEmail(2);
|
||||
$this->assertCount(2, $emails);
|
||||
$this->assertEquals('custommailer@appwrite.io', $emails[0]['from'][0]['address']);
|
||||
$this->assertEquals('Custom Mailer', $emails[0]['from'][0]['name']);
|
||||
$this->assertEquals('reply@appwrite.io', $emails[0]['replyTo'][0]['address']);
|
||||
$this->assertEquals('Custom Mailer', $emails[0]['replyTo'][0]['name']);
|
||||
$this->assertEquals('Custom SMTP email sample', $emails[0]['subject']);
|
||||
$this->assertStringContainsStringIgnoringCase('working correctly', $emails[0]['text']);
|
||||
$this->assertStringContainsStringIgnoringCase('working correctly', $emails[0]['html']);
|
||||
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $emails[0]['text']);
|
||||
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $emails[0]['html']);
|
||||
$smtpProbe = function ($email) {
|
||||
$this->assertEquals('Custom SMTP email sample', $email['subject']);
|
||||
};
|
||||
$email1 = $this->getLastEmailByAddress('testuser@appwrite.io', $smtpProbe);
|
||||
$email2 = $this->getLastEmailByAddress('testusertwo@appwrite.io', $smtpProbe);
|
||||
|
||||
$to = [
|
||||
$emails[0]['to'][0]['address'],
|
||||
$emails[1]['to'][0]['address']
|
||||
];
|
||||
\sort($to);
|
||||
|
||||
$this->assertEquals('testuser@appwrite.io', $to[0]);
|
||||
$this->assertEquals('testusertwo@appwrite.io', $to[1]);
|
||||
$this->assertEquals('custommailer@appwrite.io', $email1['from'][0]['address']);
|
||||
$this->assertEquals('Custom Mailer', $email1['from'][0]['name']);
|
||||
$this->assertEquals('reply@appwrite.io', $email1['replyTo'][0]['address']);
|
||||
$this->assertEquals('Custom Mailer', $email1['replyTo'][0]['name']);
|
||||
$this->assertEquals('Custom SMTP email sample', $email1['subject']);
|
||||
$this->assertStringContainsStringIgnoringCase('working correctly', $email1['text']);
|
||||
$this->assertStringContainsStringIgnoringCase('working correctly', $email1['html']);
|
||||
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $email1['text']);
|
||||
$this->assertStringContainsStringIgnoringCase('251 Little Falls Drive', $email1['html']);
|
||||
$this->assertEquals('custommailer@appwrite.io', $email2['from'][0]['address']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/projects/' . $id . '/smtp/tests', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
@@ -6427,11 +6423,12 @@ class ProjectsConsoleClientTest extends Scope
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
|
||||
$lastEmail = $this->getLastEmail(1, function ($email) use ($url) {
|
||||
$userEmail = $this->getUser()['email'];
|
||||
|
||||
$lastEmail = $this->getLastEmailByAddress($userEmail, function ($email) use ($url) {
|
||||
$this->assertStringContainsString($url, $email['html'] ?? '');
|
||||
});
|
||||
|
||||
$this->assertEquals($this->getUser()['email'], $lastEmail['to'][0]['address']);
|
||||
$this->assertEquals('Password Reset for ' . $this->getProject()['name'], $lastEmail['subject']);
|
||||
|
||||
$expectedUrl = $url . "&userId=" . $userId . "&secret=";
|
||||
@@ -6450,7 +6447,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], $this->getHeaders()),
|
||||
[
|
||||
'userId' => ID::unique(),
|
||||
'email' => $this->getUser()['email'],
|
||||
'email' => $userEmail,
|
||||
'url' => $url,
|
||||
]
|
||||
);
|
||||
@@ -6460,11 +6457,10 @@ class ProjectsConsoleClientTest extends Scope
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
|
||||
$lastEmail = $this->getLastEmail(1, function ($email) use ($url) {
|
||||
$lastEmail = $this->getLastEmailByAddress($userEmail, function ($email) use ($url) {
|
||||
$this->assertStringContainsString($url, $email['html'] ?? '');
|
||||
});
|
||||
|
||||
$this->assertEquals($this->getUser()['email'], $lastEmail['to'][0]['address']);
|
||||
$this->assertEquals('Password Reset for ' . $this->getProject()['name'], $lastEmail['subject']);
|
||||
|
||||
$expectedUrl = $url . "&userId=" . $userId . "&secret=";
|
||||
@@ -6483,7 +6479,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], $this->getHeaders()),
|
||||
[
|
||||
'userId' => ID::unique(),
|
||||
'email' => $this->getUser()['email'],
|
||||
'email' => $userEmail,
|
||||
'url' => $url,
|
||||
]
|
||||
);
|
||||
@@ -6493,11 +6489,10 @@ class ProjectsConsoleClientTest extends Scope
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
|
||||
$lastEmail = $this->getLastEmail(1, function ($email) use ($url, $userId) {
|
||||
$lastEmail = $this->getLastEmailByAddress($userEmail, function ($email) use ($url, $userId) {
|
||||
$this->assertStringContainsString($url . '?userId=' . $userId, $email['html'] ?? '');
|
||||
});
|
||||
|
||||
$this->assertEquals($this->getUser()['email'], $lastEmail['to'][0]['address']);
|
||||
$this->assertEquals('Password Reset for ' . $this->getProject()['name'], $lastEmail['subject']);
|
||||
|
||||
$expectedUrl = $url . "?userId=" . $userId . "&secret=";
|
||||
@@ -6516,7 +6511,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], $this->getHeaders()),
|
||||
[
|
||||
'userId' => ID::unique(),
|
||||
'email' => $this->getUser()['email'],
|
||||
'email' => $userEmail,
|
||||
'url' => $url,
|
||||
]
|
||||
);
|
||||
@@ -6526,11 +6521,10 @@ class ProjectsConsoleClientTest extends Scope
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
|
||||
$lastEmail = $this->getLastEmail(1, function ($email) use ($url, $userId) {
|
||||
$lastEmail = $this->getLastEmailByAddress($userEmail, function ($email) use ($url, $userId) {
|
||||
$this->assertStringContainsString($url . '?userId=' . $userId, $email['html'] ?? '');
|
||||
});
|
||||
|
||||
$this->assertEquals($this->getUser()['email'], $lastEmail['to'][0]['address']);
|
||||
$this->assertEquals('Password Reset for ' . $this->getProject()['name'], $lastEmail['subject']);
|
||||
|
||||
$expectedUrl = $url . "?userId=" . $userId . "&secret=";
|
||||
@@ -6549,7 +6543,7 @@ class ProjectsConsoleClientTest extends Scope
|
||||
], $this->getHeaders()),
|
||||
[
|
||||
'userId' => ID::unique(),
|
||||
'email' => $this->getUser()['email'],
|
||||
'email' => $userEmail,
|
||||
'url' => $url,
|
||||
]
|
||||
);
|
||||
@@ -6559,11 +6553,10 @@ class ProjectsConsoleClientTest extends Scope
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
|
||||
$lastEmail = $this->getLastEmail(1, function ($email) {
|
||||
$lastEmail = $this->getLastEmailByAddress($userEmail, function ($email) {
|
||||
$this->assertStringContainsString('INJECTED', $email['html'] ?? '');
|
||||
});
|
||||
|
||||
$this->assertEquals($this->getUser()['email'], $lastEmail['to'][0]['address']);
|
||||
$this->assertEquals('Password Reset for ' . $this->getProject()['name'], $lastEmail['subject']);
|
||||
|
||||
$this->assertStringContainsString('INJECTED', $lastEmail['html']);
|
||||
|
||||
@@ -16,26 +16,39 @@ trait SchedulesBase
|
||||
return self::$cachedScheduleProjectData;
|
||||
}
|
||||
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => ID::unique(),
|
||||
'name' => 'Schedule Test Team',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $team['headers']['status-code']);
|
||||
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Schedule Test Project',
|
||||
'teamId' => $team['body']['$id'],
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
]);
|
||||
$teamId = ID::unique();
|
||||
$team = null;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => $teamId,
|
||||
'name' => 'Schedule Test Team',
|
||||
]);
|
||||
if (\in_array($team['headers']['status-code'], [201, 409])) {
|
||||
break;
|
||||
}
|
||||
\usleep(500000);
|
||||
}
|
||||
$this->assertContains($team['headers']['status-code'], [201, 409]);
|
||||
|
||||
$project = null;
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$project = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'projectId' => ID::unique(),
|
||||
'name' => 'Schedule Test Project',
|
||||
'teamId' => $team['body']['$id'] ?? $teamId,
|
||||
'region' => System::getEnv('_APP_REGION', 'default'),
|
||||
]);
|
||||
if ($project['headers']['status-code'] === 201) {
|
||||
break;
|
||||
}
|
||||
\usleep(500000);
|
||||
}
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
|
||||
$projectId = $project['body']['$id'];
|
||||
|
||||
Reference in New Issue
Block a user