1
0
Fork 0
mirror of synced 2024-06-01 10:29:48 +12:00

feat: event classes for different cases

This commit is contained in:
Torsten Dittmann 2022-04-13 14:39:31 +02:00
parent 2f9b9445dd
commit 87b8fc3c41
31 changed files with 1420 additions and 1038 deletions

View file

@ -60,7 +60,7 @@ App::post('/v1/account')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
@ -120,11 +120,8 @@ App::post('/v1/account')
Authorization::setRole('role:' . Auth::USER_ROLE_MEMBER);
$audits
->setParam('userId', $user->getId())
->setResource('user/'.$user->getId())
->setUser($user)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$usage
@ -169,7 +166,7 @@ App::post('/v1/account/sessions')
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
@ -215,12 +212,8 @@ App::post('/v1/account/sessions')
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile);
$audits
->setParam('userId', $profile->getId())
->setParam('sessionId', $session->getId())
->setResource('user/'.$profile->getId())
->setUser($profile)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$profile->getId()
]))
;
if (!Config::getParam('domainVerification')) {
@ -399,7 +392,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -577,12 +570,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setResource('user/'.$user->getId())
->setUser($user)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$usage
@ -656,9 +645,9 @@ App::post('/v1/account/sessions/magic-url')
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $mails */
/** @var Appwrite\Event\Mail $mails */
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
@ -703,8 +692,6 @@ App::post('/v1/account/sessions/magic-url')
'search' => implode(' ', [$userId, $email]),
'deleted' => false
])));
$mails->setParam('event', 'users.create');
}
$loginSecret = Auth::tokenGenerator();
@ -740,14 +727,10 @@ App::post('/v1/account/sessions/magic-url')
$url = Template::unParseURL($url);
$mails
->setPayload([
'from' => $project->getId(),
'recipient' => $user->getAttribute('email'),
'url' => $url,
'locale' => $locale->default,
'project' => $project->getAttribute('name', ['[APP-NAME]']),
'type' => MAIL_TYPE_MAGIC_SESSION
])
->setType(MAIL_TYPE_MAGIC_SESSION)
->setRecipient($user->getAttribute('email'))
->setUrl($url)
->setLocale($locale->default)
->trigger()
;
@ -763,10 +746,8 @@ App::post('/v1/account/sessions/magic-url')
($isPrivilegedUser || $isAppUser) ? $loginSecret : '');
$audits
->setResource('user/'.$user->getId())
->setUser($user)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$response
@ -806,7 +787,7 @@ App::put('/v1/account/sessions/magic-url')
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$user = $dbForProject->getDocument('users', $userId);
@ -871,13 +852,7 @@ App::put('/v1/account/sessions/magic-url')
throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR);
}
$audits
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$audits->setResource('user/'.$user->getId());
$events
->setParam('userId', $user->getId())
@ -943,7 +918,7 @@ App::post('/v1/account/sessions/anonymous')
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Stats\Stats $events */
@ -1022,13 +997,7 @@ App::post('/v1/account/sessions/anonymous')
$user = $dbForProject->updateDocument('users', $user->getId(),
$user->setAttribute('sessions', $session, Document::SET_TYPE_APPEND));
$audits
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$audits->setResource('user/'.$user->getId());
$usage
->setParam('users.sessions.create', 1)
@ -1350,7 +1319,7 @@ App::patch('/v1/account/name')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Stats\Stats $events */
@ -1360,20 +1329,12 @@ App::patch('/v1/account/name')
);
$audits
->setParam('userId', $user->getId())
->setResource('user/'.$user->getId())
->setUser($user)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$usage
->setParam('users.update', 1)
;
$events
->setParam('userId', $user->getId())
;
$usage->setParam('users.update', 1);
$events->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_USER);
});
@ -1402,7 +1363,7 @@ App::patch('/v1/account/password')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Stats\Stats $events */
@ -1417,20 +1378,12 @@ App::patch('/v1/account/password')
);
$audits
->setParam('userId', $user->getId())
->setResource('user/'.$user->getId())
->setUser($user)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$usage
->setParam('users.update', 1)
;
$events
->setParam('userId', $user->getId())
;
$usage->setParam('users.update', 1);
$events->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_USER);
});
@ -1459,7 +1412,7 @@ App::patch('/v1/account/email')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Stats\Stats $events */
@ -1491,20 +1444,12 @@ App::patch('/v1/account/email')
}
$audits
->setParam('userId', $user->getId())
->setResource('user/'.$user->getId())
->setUser($user)
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$usage
->setParam('users.update', 1)
;
$events
->setParam('userId', $user->getId())
;
$usage->setParam('users.update', 1);
$events->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_USER);
});
@ -1532,26 +1477,15 @@ App::patch('/v1/account/prefs')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
$audits
->setParam('userId', $user->getId())
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
;
$usage
->setParam('users.update', 1)
;
$events
->setParam('userId', $user->getId())
;
$audits->setResource('user/'.$user->getId());
$usage->setParam('users.update', 1);
$events->setParam('userId', $user->getId());
$response->dynamic($user, Response::MODEL_USER);
});
@ -1579,7 +1513,7 @@ App::delete('/v1/account')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -1597,10 +1531,8 @@ App::delete('/v1/account')
*/
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.delete')
->setParam('resource', 'user/' . $user->getId())
->setParam('data', $user->getArrayCopy())
->setResource('user/' . $user->getId())
->setPayload($response->output($user, Response::MODEL_USER))
;
$events
@ -1609,14 +1541,11 @@ App::delete('/v1/account')
;
if (!Config::getParam('domainVerification')) {
$response
->addHeader('X-Fallback-Cookies', \json_encode([]))
;
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
}
$usage
->setParam('users.delete', 1)
;
$usage->setParam('users.delete', 1);
$response
->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
@ -1651,7 +1580,7 @@ App::delete('/v1/account/sessions/:sessionId')
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -1668,11 +1597,7 @@ App::delete('/v1/account/sessions/:sessionId')
$dbForProject->deleteDocument('sessions', $session->getId());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.sessions.delete')
->setParam('resource', 'user/' . $user->getId())
;
$audits->setResource('user/' . $user->getId());
$session->setAttribute('current', false);
@ -1697,9 +1622,9 @@ App::delete('/v1/account/sessions/:sessionId')
$dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('sessions', $sessions));
$events
->setPayload($response->output($session, Response::MODEL_SESSION))
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setPayload($response->output($session, Response::MODEL_SESSION))
;
$usage
@ -1744,7 +1669,7 @@ App::patch('/v1/account/sessions/:sessionId')
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -1773,7 +1698,7 @@ App::patch('/v1/account/sessions/:sessionId')
$appSecret = $project->getAttribute('providers', [])[$provider.'Secret'] ?? '{}';
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
if (!\class_exists($className)) {
throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
@ -1793,16 +1718,12 @@ App::patch('/v1/account/sessions/:sessionId')
$user->setAttribute("sessions", $sessions);
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.sessions.update')
->setParam('resource', 'user/' . $user->getId())
;
$audits->setResource('user/' . $user->getId());
$events
->setPayload($response->output($session, Response::MODEL_SESSION))
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setPayload($response->output($session, Response::MODEL_SESSION))
;
$usage
@ -1843,7 +1764,7 @@ App::delete('/v1/account/sessions')
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -1853,11 +1774,7 @@ App::delete('/v1/account/sessions')
foreach ($sessions as $session) {/** @var Document $session */
$dbForProject->deleteDocument('sessions', $session->getId());
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.sessions.delete')
->setParam('resource', 'user/' . $user->getId())
;
$audits->setResource('user/' . $user->getId());
if (!Config::getParam('domainVerification')) {
$response
@ -1884,18 +1801,19 @@ App::delete('/v1/account/sessions')
$numOfSessions = count($sessions);
$events
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
->setPayload($response->output(new Document([
'sessions' => $sessions,
'total' => $numOfSessions,
]), Response::MODEL_SESSION_LIST))
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId())
;
$usage
->setParam('users.sessions.delete', $numOfSessions)
->setParam('users.update', 1)
;
$response->noContent();
});
@ -1930,8 +1848,8 @@ App::post('/v1/account/recovery')
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $mails */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Mail $mails */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -1978,40 +1896,33 @@ App::post('/v1/account/recovery')
$url = Template::unParseURL($url);
$mails
->setPayload([
'from' => $project->getId(),
'recipient' => $profile->getAttribute('email', ''),
'name' => $profile->getAttribute('name', ''),
'url' => $url,
'locale' => $locale->default,
'project' => $project->getAttribute('name', ['[APP-NAME]']),
'type' => MAIL_TYPE_RECOVERY
])
->setType(MAIL_TYPE_RECOVERY)
->setRecipient($profile->getAttribute('email', ''))
->setUrl($url)
->setLocale($locale->default)
->setName($profile->getAttribute('name'))
->trigger();
;
$events
->setParam('eventData',
$response->output($recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
->setPayload($response->output(
$recovery->setAttribute('secret', $secret),
Response::MODEL_TOKEN
))
;
$recovery // Hide secret for clients, sp
->setAttribute('secret',
($isPrivilegedUser || $isAppUser) ? $secret : '');
$audits
->setParam('userId', $profile->getId())
->setParam('event', 'account.recovery.create')
->setParam('resource', 'user/' . $profile->getId())
;
$audits->setResource('user/' . $profile->getId());
$usage
->setParam('users.update', 1)
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($recovery, Response::MODEL_TOKEN);
});
@ -2042,7 +1953,7 @@ App::put('/v1/account/recovery')
->action(function ($userId, $secret, $password, $passwordAgain, $response, $dbForProject, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
@ -2084,20 +1995,15 @@ App::put('/v1/account/recovery')
$dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
$audits
->setParam('userId', $profile->getId())
->setParam('event', 'account.recovery.update')
->setParam('resource', 'user/' . $profile->getId())
;
$audits->setResource('user/' . $profile->getId());
$usage
->setParam('users.update', 1)
;
$usage->setParam('users.update', 1);
$events
->setParam('userId', $profile->getId())
->setParam('tokenId', $recovery->getId())
;
$response->dynamic($recovery, Response::MODEL_TOKEN);
});
@ -2133,9 +2039,9 @@ App::post('/v1/account/verification')
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $mails */
/** @var Appwrite\Event\Mail $mails */
/** @var Appwrite\Stats\Stats $usage */
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
@ -2171,40 +2077,30 @@ App::post('/v1/account/verification')
$url = Template::unParseURL($url);
$mails
->setPayload([
'from' => $project->getId(),
'recipient' => $user->getAttribute('email'),
'name' => $user->getAttribute('name'),
'url' => $url,
'locale' => $locale->default,
'project' => $project->getAttribute('name', ['[APP-NAME]']),
'type' => MAIL_TYPE_VERIFICATION
])
->setType(MAIL_TYPE_VERIFICATION)
->setRecipient($user->getAttribute('email'))
->setUrl($url)
->setLocale($locale->default)
->setName($user->getAttribute('name'))
->trigger()
;
$events
->setParam('eventData',
$response->output($verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN
))
->setParam('userId', $user->getId())
->setParam('tokenId', $verification->getId())
->setPayload($response->output(
$verification->setAttribute('secret', $verificationSecret),
Response::MODEL_TOKEN
))
;
$verification // Hide secret for clients, sp
->setAttribute('secret',
($isPrivilegedUser || $isAppUser) ? $verificationSecret : '');
$audits
->setParam('userId', $user->getId())
->setParam('event', 'account.verification.create')
->setParam('resource', 'user/' . $user->getId())
;
$audits->setResource('user/' . $user->getId());
$usage->setParam('users.update', 1);
$usage
->setParam('users.update', 1)
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($verification, Response::MODEL_TOKEN);
});
@ -2235,7 +2131,7 @@ App::put('/v1/account/verification')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
@ -2269,15 +2165,9 @@ App::put('/v1/account/verification')
$dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('tokens', $tokens));
$audits
->setParam('userId', $profile->getId())
->setParam('event', 'account.verification.update')
->setParam('resource', 'user/' . $user->getId())
;
$audits->setResource('user/' . $user->getId());
$usage
->setParam('users.update', 1)
;
$usage->setParam('users.update', 1);
$events
->setParam('userId', $user->getId())

View file

@ -33,6 +33,8 @@ use Appwrite\Network\Validator\URL;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use Appwrite\Detector\Detector;
use Appwrite\Event\Audit as EventAudit;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Stats\Stats;
@ -43,13 +45,13 @@ use Appwrite\Stats\Stats;
* @param Utopia\Database\Document $attribute
* @param Appwrite\Utopia\Response $response
* @param Utopia\Database\Database $dbForProject
* @param Appwrite\Event\Event $database
* @param Appwrite\Event\Event $audits
* @param Appwrite\Event\Database $database
* @param Appwrite\Event\Audit $audits
* @param Appwrite\Stats\Stats $usage
*
* @return Document Newly created attribute document
*/
function createAttribute(string $collectionId, Document $attribute, Response $response, Database $dbForProject, Event $database, Event $audits, Stats $usage): Document
function createAttribute(string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Event $events, Stats $usage): Document
{
$key = $attribute->getAttribute('key');
$type = $attribute->getAttribute('type', '');
@ -113,22 +115,23 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
$dbForProject->deleteCachedDocument('collections', $collectionId);
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
// Pass clone of $attribute object to workers
// so we can later modify Document to fit response model
$clone = clone $attribute;
$database
->setParam('type', DATABASE_TYPE_CREATE_ATTRIBUTE)
->setParam('collection', $collection)
->setParam('document', $clone)
;
$usage->setParam('database.collections.update', 1);
$database
->setType(DATABASE_TYPE_CREATE_ATTRIBUTE)
->setCollection($collection)
->setDocument($attribute)
;
$events
->setTrigger($collection)
->setParam('collectionId', $collection->getId())
->setParam('attributeId', $attribute->getId())
;
$audits
->setParam('event', 'database.attributes.create')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $clone)
->setResource('collection/'.$collectionId)
->setPayload($attribute->getArrayCopy())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -139,7 +142,7 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
App::post('/v1/database/collections')
->desc('Create Collection')
->groups(['api', 'database'])
->label('event', 'database.collections.create')
->label('event', 'collections.[collectionId].create')
->label('scope', 'collections.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
@ -157,11 +160,13 @@ App::post('/v1/database/collections')
->inject('dbForProject')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForProject, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForProject, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId;
@ -187,11 +192,11 @@ App::post('/v1/database/collections')
}
$audits
->setParam('event', 'database.collections.create')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $collection->getArrayCopy())
->setResource('collection/'.$collectionId)
->setPayload($collection->getArrayCopy())
;
$events->setParam('collectionId', $collection->getId());
$usage->setParam('database.collections.create', 1);
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -581,7 +586,7 @@ App::put('/v1/database/collections/:collectionId')
->desc('Update Collection')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'database.collections.update')
->label('event', 'collections.[collectionId].update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'updateCollection')
@ -599,11 +604,13 @@ App::put('/v1/database/collections/:collectionId')
->inject('dbForProject')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $name, $permission, $read, $write, $enabled, $response, $dbForProject, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $name, $permission, $read, $write, $enabled, $response, $dbForProject, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$collection = $dbForProject->getDocument('collections', $collectionId);
@ -633,14 +640,14 @@ App::put('/v1/database/collections/:collectionId')
throw new Exception('Bad structure. '.$exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
}
$usage->setParam('database.collections.update', 1);
$audits
->setParam('event', 'database.collections.update')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $collection->getArrayCopy())
->setResource('collection/'.$collectionId)
->setPayload($collection->getArrayCopy())
;
$usage->setParam('database.collections.update', 1);
$events->setParam('collectionId', $collection->getId());
$response->dynamic($collection, Response::MODEL_COLLECTION);
});
@ -648,7 +655,7 @@ App::delete('/v1/database/collections/:collectionId')
->desc('Delete Collection')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'database.collections.delete')
->label('event', 'collections.[collectionId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'deleteCollection')
@ -666,8 +673,8 @@ App::delete('/v1/database/collections/:collectionId')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
$collection = $dbForProject->getDocument('collections', $collectionId);
@ -682,20 +689,22 @@ App::delete('/v1/database/collections/:collectionId')
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $collection)
->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $collection
])
;
$usage->setParam('database.collections.delete', 1);
$events
->setParam('eventData', $response->output($collection, Response::MODEL_COLLECTION))
->setParam('collectionId', $collection->getId())
->setPayload($response->output($collection, Response::MODEL_COLLECTION))
;
$audits
->setParam('event', 'database.collections.delete')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $collection->getArrayCopy())
->setResource('collection/'.$collectionId)
->setPayload($collection->getArrayCopy())
;
$response->noContent();
@ -704,7 +713,7 @@ App::delete('/v1/database/collections/:collectionId')
App::post('/v1/database/collections/:collectionId/attributes/string')
->desc('Create String Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
@ -724,12 +733,14 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $size, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $size, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
// Ensure attribute default is within required size
$validator = new Text($size);
@ -744,7 +755,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
'required' => $required,
'default' => $default,
'array' => $array,
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_STRING);
});
@ -752,7 +763,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
App::post('/v1/database/collections/:collectionId/attributes/email')
->desc('Create Email Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -771,11 +782,12 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
@ -786,7 +798,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_EMAIL,
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_EMAIL);
});
@ -794,7 +806,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
App::post('/v1/database/collections/:collectionId/attributes/enum')
->desc('Create Enum Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -814,12 +826,14 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $elements, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $elements, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
// use length of longest string as attribute size
$size = 0;
@ -845,7 +859,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_ENUM,
'formatOptions' => ['elements' => $elements],
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_ENUM);
});
@ -853,7 +867,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
App::post('/v1/database/collections/:collectionId/attributes/ip')
->desc('Create IP Address Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -872,11 +886,12 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
$attribute = createAttribute($collectionId, new Document([
@ -887,7 +902,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_IP,
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_IP);
});
@ -895,7 +910,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
App::post('/v1/database/collections/:collectionId/attributes/url')
->desc('Create URL Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -914,11 +929,13 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$attribute = createAttribute($collectionId, new Document([
'key' => $key,
@ -928,7 +945,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_URL,
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_URL);
});
@ -936,7 +953,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
App::post('/v1/database/collections/:collectionId/attributes/integer')
->desc('Create Integer Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -957,12 +974,14 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
// Ensure attribute default is within range
$min = (is_null($min)) ? PHP_INT_MIN : \intval($min);
@ -992,7 +1011,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
'min' => $min,
'max' => $max,
],
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$formatOptions = $attribute->getAttribute('formatOptions', []);
@ -1007,7 +1026,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
App::post('/v1/database/collections/:collectionId/attributes/float')
->desc('Create Float Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -1027,13 +1046,15 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
->inject('dbForProject')
->inject('database')
->inject('audits')
->inject('events')
->inject('usage')
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $database, $audits, $events, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
// Ensure attribute default is within range
$min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min);
@ -1066,7 +1087,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
'min' => $min,
'max' => $max,
],
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$formatOptions = $attribute->getAttribute('formatOptions', []);
@ -1081,7 +1102,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
App::post('/v1/database/collections/:collectionId/attributes/boolean')
->desc('Create Boolean Attribute')
->groups(['api', 'database'])
->label('event', 'database.attributes.create')
->label('event', 'collections.[collectionId].attributes.[attributeId].create')
->label('scope', 'collections.write')
->label('sdk.namespace', 'database')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
@ -1100,12 +1121,14 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject*/
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$attribute = createAttribute($collectionId, new Document([
'key' => $key,
@ -1114,7 +1137,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
'required' => $required,
'default' => $default,
'array' => $array,
]), $response, $dbForProject, $database, $audits, $usage);
]), $response, $dbForProject, $database, $audits, $events, $usage);
$response->dynamic($attribute, Response::MODEL_ATTRIBUTE_BOOLEAN);
});
@ -1221,7 +1244,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
->desc('Delete Attribute')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'database.attributes.delete')
->label('event', 'collections.[collectionId].attributes.[attributeId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'deleteAttribute')
@ -1239,9 +1262,9 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
->action(function ($collectionId, $key, $response, $dbForProject, $database, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
$collection = $dbForProject->getDocument('collections', $collectionId);
@ -1265,9 +1288,9 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
$dbForProject->deleteCachedCollection('collection_' . $collection->getInternalId());
$database
->setParam('type', DATABASE_TYPE_DELETE_ATTRIBUTE)
->setParam('collection', $collection)
->setParam('document', $attribute)
->setType(DATABASE_TYPE_DELETE_ATTRIBUTE)
->setCollection($collection)
->setDocument($attribute)
;
$usage->setParam('database.collections.update', 1);
@ -1291,13 +1314,15 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
};
$events
->setParam('payload', $response->output($attribute, $model))
->setParam('collectionId', $collection->getId())
->setParam('attributeId', $attribute->getId())
->setTrigger($collection)
->setPayload($response->output($attribute, $model))
;
$audits
->setParam('event', 'database.attributes.delete')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $attribute->getArrayCopy())
->setResource('collection/'.$collectionId)
->setPayload($attribute->getArrayCopy())
;
$response->noContent();
@ -1306,7 +1331,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
App::post('/v1/database/collections/:collectionId/indexes')
->desc('Create Index')
->groups(['api', 'database'])
->label('event', 'database.indexes.create')
->label('event', 'collections.[collectionId].indexes.[indexId].create')
->label('scope', 'collections.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
@ -1325,12 +1350,14 @@ App::post('/v1/database/collections/:collectionId/indexes')
->inject('database')
->inject('audits')
->inject('usage')
->action(function ($collectionId, $key, $type, $attributes, $orders, $response, $dbForProject, $database, $audits, $usage) {
->inject('events')
->action(function ($collectionId, $key, $type, $attributes, $orders, $response, $dbForProject, $database, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Stats\Stats $audits */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$collection = $dbForProject->getDocument('collections', $collectionId);
@ -1393,17 +1420,22 @@ App::post('/v1/database/collections/:collectionId/indexes')
$dbForProject->deleteCachedDocument('collections', $collectionId);
$database
->setParam('type', DATABASE_TYPE_CREATE_INDEX)
->setParam('collection', $collection)
->setParam('document', $index)
->setType(DATABASE_TYPE_CREATE_INDEX)
->setCollection($collection)
->setDocument($index)
;
$usage->setParam('database.collections.update', 1);
$events
->setParam('collectionId', $collection->getId())
->setParam('indexId', $index->getId())
->setTrigger($collection)
;
$audits
->setParam('event', 'database.indexes.create')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $index->getArrayCopy())
->setResource('collection/'.$collection->getId())
->setPayload($index->getArrayCopy())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -1493,7 +1525,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
->desc('Delete Index')
->groups(['api', 'database'])
->label('scope', 'collections.write')
->label('event', 'database.indexes.delete')
->label('event', 'collections.[collectionId].indexes.[indexId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'database')
->label('sdk.method', 'deleteIndex')
@ -1511,9 +1543,9 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
->action(function ($collectionId, $key, $response, $dbForProject, $database, $events, $audits, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Database $database */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
$collection = $dbForProject->getDocument('collections', $collectionId);
@ -1536,21 +1568,23 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
$dbForProject->deleteCachedDocument('collections', $collectionId);
$database
->setParam('type', DATABASE_TYPE_DELETE_INDEX)
->setParam('collection', $collection)
->setParam('document', $index)
->setType(DATABASE_TYPE_DELETE_INDEX)
->setCollection($collection)
->setDocument($index)
;
$usage->setParam('database.collections.update', 1);
$events
->setParam('payload', $response->output($index, Response::MODEL_INDEX))
->setParam('collectionId', $collection->getId())
->setParam('indexId', $index->getId())
->setTrigger($collection)
->setPayload($response->output($index, Response::MODEL_INDEX))
;
$audits
->setParam('event', 'database.indexes.delete')
->setParam('resource', 'collection/'.$collectionId)
->setParam('data', $index->getArrayCopy())
->setResource('collection/'.$collection->getId())
->setPayload($index->getArrayCopy())
;
$response->noContent();
@ -1559,7 +1593,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
App::post('/v1/database/collections/:collectionId/documents')
->desc('Create Document')
->groups(['api', 'database'])
->label('event', 'database.documents.create')
->label('event', 'collections.[collectionId].documents.[documentId].create')
->label('scope', 'documents.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'database')
@ -1584,7 +1618,7 @@ App::post('/v1/database/collections/:collectionId/documents')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
/** @var string $mode */
@ -1658,7 +1692,11 @@ App::post('/v1/database/collections/:collectionId/documents')
throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS);
}
$events->setParam('collection', $collection->getArrayCopy());
$events
->setParam('collectionId', $collection->getId())
->setParam('documentId', $document->getId())
->setTrigger($collection)
;
$usage
->setParam('database.documents.create', 1)
@ -1666,9 +1704,8 @@ App::post('/v1/database/collections/:collectionId/documents')
;
$audits
->setParam('event', 'database.documents.create')
->setParam('resource', 'document/'.$document->getId())
->setParam('data', $document->getArrayCopy())
->setResource('document/'.$document->getId())
->setPayload($document->getArrayCopy())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -1938,7 +1975,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Update Document')
->groups(['api', 'database'])
->label('event', 'database.documents.update')
->label('event', 'collections.[collectionId].documents.[documentId].update')
->label('scope', 'documents.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'database')
@ -1961,7 +1998,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForProject, $audits, $usage, $events, $mode) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
/** @var string $mode */
@ -1999,7 +2036,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
if (empty($data)) {
throw new Exception('Missing payload', 400, Exception::DOCUMENT_MISSING_PAYLOAD);
}
if (!\is_array($data)) {
throw new Exception('Data param should be a valid JSON object', 400, Exception::DOCUMENT_INVALID_STRUCTURE);
}
@ -2053,17 +2090,20 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
}
$events->setParam('collection', $collection->getArrayCopy());
$events
->setParam('collectionId', $collection->getId())
->setParam('documentId', $document->getId())
->setTrigger($collection)
;
$usage
->setParam('database.documents.update', 1)
->setParam('collectionId', $collectionId)
;
;
$audits
->setParam('event', 'database.documents.update')
->setParam('resource', 'document/'.$document->getId())
->setParam('data', $document->getArrayCopy())
->setResource('document/'.$document->getId())
->setPayload($document->getArrayCopy())
;
$response->dynamic($document, Response::MODEL_DOCUMENT);
@ -2073,7 +2113,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
->desc('Delete Document')
->groups(['api', 'database'])
->label('scope', 'documents.write')
->label('event', 'database.documents.delete')
->label('event', 'collections.[collectionId].documents.[documentId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'database')
->label('sdk.method', 'deleteDocument')
@ -2093,7 +2133,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Stats\Stats $usage */
/** @var string $mode */
@ -2142,24 +2182,27 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
$document->setAttribute('$collection', $collectionId);
$deletes
->setParam('type', DELETE_TYPE_AUDIT)
->setParam('document', $document)
->setPayload([
'type' => DELETE_TYPE_AUDIT,
'document' => $document
])
;
$usage
->setParam('database.documents.delete', 1)
->setParam('collectionId', $collectionId)
;
;
$events
->setParam('eventData', $response->output($document, Response::MODEL_DOCUMENT))
->setParam('collection', $collection->getArrayCopy());
->setParam('collectionId', $collection->getId())
->setParam('documentId', $document->getId())
->setTrigger($collection)
->setPayload($response->output($document, Response::MODEL_DOCUMENT))
;
$audits
->setParam('event', 'database.documents.delete')
->setParam('resource', 'document/'.$document->getId())
->setParam('data', $document->getArrayCopy()) // Audit document in case of malicious or disastrous action
->setResource('document/'.$document->getId())
->setPayload($document->getArrayCopy())
;
$response->noContent();

View file

@ -3,6 +3,7 @@
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Event\Event;
use Appwrite\Event\Validator\Event as ValidatorEvent;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Validator\CustomId;
use Utopia\Database\Validator\UID;
@ -34,7 +35,7 @@ App::post('/v1/functions')
->groups(['api', 'functions'])
->desc('Create Function')
->label('scope', 'functions.write')
->label('event', 'functions.create')
->label('event', 'functions.[functionId].create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'create')
@ -47,14 +48,16 @@ App::post('/v1/functions')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('events', [], new ArrayList(new ValidatorEvent()), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.', true)
->inject('response')
->inject('dbForProject')
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject) {
->inject('events')
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject, $eventsInstance) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $eventsInstance */
$functionId = ($functionId == 'unique()') ? $dbForProject->getId() : $functionId;
$function = $dbForProject->createDocument('functions', new Document([
@ -75,6 +78,8 @@ App::post('/v1/functions')
'search' => implode(' ', [$functionId, $name, $runtime]),
]));
$eventsInstance->setParam('functionId', $function->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($function, Response::MODEL_FUNCTION);
});
@ -144,7 +149,7 @@ App::get('/v1/functions/runtimes')
return $runtimes[$key];
}, array_keys($runtimes));
$response->dynamic(new Document([
$response->dynamic(new Document([
'total' => count($runtimes),
'runtimes' => $runtimes
]), Response::MODEL_RUNTIME_LIST);
@ -202,9 +207,9 @@ App::get('/v1/functions/:functionId/usage')
if ($function->isEmpty()) {
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
}
$usage = [];
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
$periods = [
'24h' => [
'period' => '30m',
@ -223,16 +228,16 @@ App::get('/v1/functions/:functionId/usage')
'limit' => 90,
],
];
$metrics = [
"functions.$functionId.executions",
"functions.$functionId.failures",
"functions.$functionId.executions",
"functions.$functionId.failures",
"functions.$functionId.compute"
];
$stats = [];
Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
foreach ($metrics as $metric) {
$limit = $periods[$range]['limit'];
$period = $periods[$range]['period'];
@ -241,7 +246,7 @@ App::get('/v1/functions/:functionId/usage')
new Query('period', Query::TYPE_EQUAL, [$period]),
new Query('metric', Query::TYPE_EQUAL, [$metric]),
], $limit, 0, ['time'], [Database::ORDER_DESC]);
$stats[$metric] = [];
foreach ($requestDocs as $requestDoc) {
$stats[$metric][] = [
@ -254,7 +259,7 @@ App::get('/v1/functions/:functionId/usage')
$backfill = $limit - \count($requestDocs);
while ($backfill > 0) {
$last = $limit - $backfill - 1; // array index of last added metric
$diff = match($period) { // convert period to seconds for unix timestamp math
$diff = match ($period) { // convert period to seconds for unix timestamp math
'30m' => 1800,
'1d' => 86400,
};
@ -265,7 +270,7 @@ App::get('/v1/functions/:functionId/usage')
$backfill--;
}
$stats[$metric] = array_reverse($stats[$metric]);
}
}
});
$usage = new Document([
@ -283,7 +288,7 @@ App::put('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Update Function')
->label('scope', 'functions.write')
->label('event', 'functions.update')
->label('event', 'functions.[functionId].update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'update')
@ -295,18 +300,20 @@ App::put('/v1/functions/:functionId')
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
->param('execute', [], new ArrayList(new Text(64)), 'An array of strings with execution permissions. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions) and get a full list of available permissions.')
->param('vars', [], new Assoc(), 'Key-value JSON object that will be passed to the function as environment variables.', true)
->param('events', [], new ArrayList(new WhiteList(array_keys(Config::getParam('events')), true)), 'Events list.', true)
->param('events', [], new ArrayList(new ValidatorEvent()), 'Events list.', true)
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('user')
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForProject, $project, $user) {
->inject('events')
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForProject, $project, $user, $eventsInstance) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
/** @var Appwrite\Auth\User $user */
/** @var Appwrite\Event\Event $eventsInstance */
$function = $dbForProject->getDocument('functions', $functionId);
@ -341,6 +348,8 @@ App::put('/v1/functions/:functionId')
]); // Async task rescheduale
}
$eventsInstance->setParam('functionId', $function->getId());
$response->dynamic($function, Response::MODEL_FUNCTION);
});
@ -348,7 +357,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->groups(['api', 'functions'])
->desc('Update Function Deployment')
->label('scope', 'functions.write')
->label('event', 'functions.deployments.update')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateDeployment')
@ -361,10 +370,12 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
->inject('response')
->inject('dbForProject')
->inject('project')
->action(function ($functionId, $deploymentId, $response, $dbForProject, $project) {
->inject('events')
->action(function ($functionId, $deploymentId, $response, $dbForProject, $project, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
/** @var Appwrite\Event\Event $events */
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
@ -404,6 +415,11 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
'trigger' => 'schedule',
]); // Async task rescheduale
}
$events
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response->dynamic($function, Response::MODEL_FUNCTION);
});
@ -411,7 +427,7 @@ App::delete('/v1/functions/:functionId')
->groups(['api', 'functions'])
->desc('Delete Function')
->label('scope', 'functions.write')
->label('event', 'functions.delete')
->label('event', 'functions.[functionId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'delete')
@ -422,10 +438,12 @@ App::delete('/v1/functions/:functionId')
->inject('response')
->inject('dbForProject')
->inject('deletes')
->action(function ($functionId, $response, $dbForProject, $deletes) {
->inject('events')
->action(function ($functionId, $response, $dbForProject, $deletes, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $events */
$function = $dbForProject->getDocument('functions', $functionId);
@ -437,10 +455,12 @@ App::delete('/v1/functions/:functionId')
throw new Exception('Failed to remove function from DB', 500, Exception::GENERAL_SERVER_ERROR);
}
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $function)
;
$deletes->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $function
]);
$events->setParam('functionId', $function->getId());
$response->noContent();
});
@ -449,7 +469,7 @@ App::post('/v1/functions/:functionId/deployments')
->groups(['api', 'functions'])
->desc('Create Deployment')
->label('scope', 'functions.write')
->label('event', 'functions.deployments.create')
->label('event', 'functions.[functionId].deployments.[deploymentId].create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createDeployment')
@ -467,16 +487,16 @@ App::post('/v1/functions/:functionId/deployments')
->inject('response')
->inject('dbForProject')
->inject('usage')
->inject('user')
->inject('events')
->inject('project')
->inject('deviceFunctions')
->inject('deviceLocal')
->action(function ($functionId, $entrypoint, $file, $activate, $request, $response, $dbForProject, $usage, $user, $project, $deviceFunctions, $deviceLocal) {
->action(function ($functionId, $entrypoint, $file, $activate, $request, $response, $dbForProject, $usage, $events, $project, $deviceFunctions, $deviceLocal) {
/** @var Utopia\Swoole\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Auth\User $user */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Database\Document $project */
/** @var Utopia\Storage\Device $deviceFunctions */
/** @var Utopia\Storage\Device $deviceLocal */
@ -515,7 +535,7 @@ App::post('/v1/functions/:functionId/deployments')
$end = $request->getContentRangeEnd();
$fileSize = $request->getContentRangeSize();
$deploymentId = $request->getHeader('x-appwrite-id', $deploymentId);
if(is_null($start) || is_null($end) || is_null($fileSize)) {
if (is_null($start) || is_null($end) || is_null($fileSize)) {
throw new Exception('Invalid content-range header', 400, Exception::STORAGE_INVALID_CONTENT_RANGE);
}
@ -539,8 +559,8 @@ App::post('/v1/functions/:functionId/deployments')
// Save to storage
$fileSize ??= $deviceLocal->getFileSize($fileTmpName);
$path = $deviceFunctions->getPath($deploymentId.'.'.\pathinfo($fileName, PATHINFO_EXTENSION));
$path = $deviceFunctions->getPath($deploymentId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$metadata = ['content_type' => $deviceLocal->getFileMimeType($fileTmpName)];
@ -560,7 +580,7 @@ App::post('/v1/functions/:functionId/deployments')
$activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN);
if($chunksUploaded === $chunks) {
if ($chunksUploaded === $chunks) {
if ($activate) {
// Remove deploy for all other deployments.
$activeDeployments = $dbForProject->find('deployments', [
@ -574,7 +594,7 @@ App::post('/v1/functions/:functionId/deployments')
$dbForProject->updateDocument('deployments', $activeDeployment->getId(), $activeDeployment);
}
}
$fileSize = $deviceFunctions->getFileSize($path);
if ($deployment->isEmpty()) {
@ -604,11 +624,9 @@ App::post('/v1/functions/:functionId/deployments')
'type' => BUILD_TYPE_DEPLOYMENT
]);
$usage
->setParam('storage', $deployment->getAttribute('size', 0))
;
$usage->setParam('storage', $deployment->getAttribute('size', 0));
} else {
if($deployment->isEmpty()) {
if ($deployment->isEmpty()) {
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$read' => ['role:all'],
@ -632,6 +650,10 @@ App::post('/v1/functions/:functionId/deployments')
$metadata = null;
$events
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($deployment, Response::MODEL_DEPLOYMENT);
});
@ -742,7 +764,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->groups(['api', 'functions'])
->desc('Delete Deployment')
->label('scope', 'functions.write')
->label('event', 'functions.deployments.delete')
->label('event', 'functions.[functionId].deployments.[deploymentId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteDeployment')
@ -755,19 +777,21 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
->inject('dbForProject')
->inject('usage')
->inject('deletes')
->inject('events')
->inject('deviceFunctions')
->action(function ($functionId, $deploymentId, $response, $dbForProject, $usage, $deletes, $deviceFunctions) {
->action(function ($functionId, $deploymentId, $response, $dbForProject, $usage, $deletes, $events, $deviceFunctions) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $events */
/** @var Utopia\Storage\Device $deviceFunctions */
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
@ -783,20 +807,24 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
}
}
if($function->getAttribute('deployment') === $deployment->getId()) { // Reset function deployment
if ($function->getAttribute('deployment') === $deployment->getId()) { // Reset function deployment
$function = $dbForProject->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
'deployment' => '',
])));
}
$usage
->setParam('storage', $deployment->getAttribute('size', 0) * -1)
;
->setParam('storage', $deployment->getAttribute('size', 0) * -1);
$events
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $deployment)
;
->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $deployment
]);
$response->noContent();
});
@ -805,7 +833,7 @@ App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('Create Execution')
->label('scope', 'execution.write')
->label('event', 'functions.executions.create')
->label('event', 'functions.[functionId].executions.[executionId].create')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createExecution')
@ -822,13 +850,15 @@ App::post('/v1/functions/:functionId/executions')
->inject('project')
->inject('dbForProject')
->inject('user')
->action(function ($functionId, $data, $async, $response, $project, $dbForProject, $user) {
->inject('events')
->action(function ($functionId, $data, $async, $response, $project, $dbForProject, $user, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $events */
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty()) {
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
@ -842,7 +872,7 @@ App::post('/v1/functions/:functionId/executions')
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400, Exception::FUNCTION_RUNTIME_UNSUPPORTED);
}
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $function->getAttribute('deployment', '')));
if ($deployment->getAttribute('resourceId') !== $function->getId()) {
throw new Exception('Deployment not found. Deploy deployment before trying to execute a function', 404, Exception::DEPLOYMENT_NOT_FOUND);
@ -853,7 +883,7 @@ App::post('/v1/functions/:functionId/executions')
}
/** Check if build has completed */
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND);
}
@ -870,7 +900,7 @@ App::post('/v1/functions/:functionId/executions')
$executionId = $dbForProject->getId();
$execution = Authorization::skip(fn() => $dbForProject->createDocument('executions', new Document([
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', new Document([
'$id' => $executionId,
'$read' => (!$user->isEmpty()) ? ['user:' . $user->getId()] : [],
'$write' => [],
@ -892,13 +922,14 @@ App::post('/v1/functions/:functionId/executions')
$sessions = $user->getAttribute('sessions', []);
$current = new Document();
foreach ($sessions as $session) { /** @var Utopia\Database\Document $session */
foreach ($sessions as $session) {
/** @var Utopia\Database\Document $session */
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$current = $session;
}
}
if(!$current->isEmpty()) {
if (!$current->isEmpty()) {
$jwtObj = new JWT(App::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
@ -907,19 +938,30 @@ App::post('/v1/functions/:functionId/executions')
}
}
if ($async) {
Resque::enqueue(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, [
'projectId' => $project->getId(),
'functionId' => $function->getId(),
'webhooks' => $project->getAttribute('webhooks', []),
'executionId' => $execution->getId(),
'trigger' => 'http',
$events
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->setPayload([
'data' => $data,
'userId' => $user->getId(),
'jwt' => $jwt,
]);
'jwt' => $jwt
])
->setTrigger($execution);
if ($async) {
$event = new Event(Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME);
$event
->setProject($project)
->setUser($user)
->setTrigger($execution)
->setPayload([
'data' => $data,
'jwt' => $jwt
])
->trigger();
$response->setStatusCode(Response::STATUS_CODE_CREATED);
return $response->dynamic($execution, Response::MODEL_EXECUTION);
}
@ -966,7 +1008,7 @@ App::post('/v1/functions/:functionId/executions')
Console::error($th->getMessage());
}
Authorization::skip(fn() => $dbForProject->updateDocument('executions', $executionId, $execution));
Authorization::skip(fn () => $dbForProject->updateDocument('executions', $executionId, $execution));
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@ -996,7 +1038,7 @@ App::get('/v1/functions/:functionId/executions')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty()) {
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
@ -1046,7 +1088,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
$function = Authorization::skip(fn() => $dbForProject->getDocument('functions', $functionId));
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
if ($function->isEmpty()) {
throw new Exception('Function not found', 404, Exception::FUNCTION_NOT_FOUND);
@ -1069,7 +1111,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->groups(['api', 'functions'])
->desc('Retry Build')
->label('scope', 'functions.write')
->label('event', 'functions.deployments.update')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'retryBuild')
@ -1082,10 +1124,12 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->inject('response')
->inject('dbForProject')
->inject('project')
->action(function ($functionId, $deploymentId, $buildId, $response, $dbForProject, $project) {
->inject('events')
->action(function ($functionId, $deploymentId, $buildId, $response, $dbForProject, $project, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $project */
/** @var Appwrite\Event\Event $events */
$function = $dbForProject->getDocument('functions', $functionId);
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
@ -1098,7 +1142,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
throw new Exception('Deployment not found', 404, Exception::DEPLOYMENT_NOT_FOUND);
}
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $buildId));
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404, Exception::BUILD_NOT_FOUND);
@ -1108,6 +1152,10 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
throw new Exception('Build not failed', 400, Exception::BUILD_IN_PROGRESS);
}
$events
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
// Enqueue a message to start the build
Resque::enqueue(Event::BUILDS_QUEUE_NAME, Event::BUILDS_CLASS_NAME, [
'projectId' => $project->getId(),
@ -1117,4 +1165,4 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
]);
$response->noContent();
});
});

View file

@ -2,6 +2,7 @@
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\Password;
use Appwrite\Event\Certificate;
use Appwrite\Event\Validator\Event;
use Appwrite\Network\Validator\CNAME;
use Appwrite\Network\Validator\Domain as DomainValidator;
@ -557,8 +558,10 @@ App::delete('/v1/projects/:projectId')
}
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $project)
->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $project
])
;
if (!$dbForConsole->deleteDocument('teams', $project->getAttribute('teamId', null))) {
@ -1390,10 +1393,12 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
$dbForConsole->deleteCachedDocument('projects', $project->getId());
// Issue a TLS certificate when domain is verified
Resque::enqueue('v1-certificates', 'CertificatesV1', [
'document' => $domain->getArrayCopy(),
'domain' => $domain->getAttribute('domain'),
]);
$event = new Certificate();
$event
->setDomain($domain)
->setValidateCNAME(true)
->setValidateTarget(true)
->trigger();
$response->dynamic($domain, Response::MODEL_DOMAIN);
});
@ -1436,8 +1441,10 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
$dbForConsole->deleteCachedDocument('projects', $project->getId());
$deletes
->setParam('type', DELETE_TYPE_CERTIFICATES)
->setParam('document', $domain)
->setPayload([
'type' => DELETE_TYPE_CERTIFICATES,
'document' => $domain
])
;
$response->noContent();

View file

@ -39,7 +39,7 @@ App::post('/v1/storage/buckets')
->desc('Create bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'storage.buckets.create')
->label('event', 'buckets.[bucketId].create')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createBucket')
@ -61,11 +61,13 @@ App::post('/v1/storage/buckets')
->inject('dbForProject')
->inject('audits')
->inject('usage')
->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage) {
->inject('events')
->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$bucketId = $bucketId === 'unique()' ? $dbForProject->getId() : $bucketId;
try {
@ -124,9 +126,12 @@ App::post('/v1/storage/buckets')
}
$audits
->setParam('event', 'storage.buckets.create')
->setParam('resource', 'storage/buckets/' . $bucket->getId())
->setParam('data', $bucket->getArrayCopy())
->setResource('storage/buckets/' . $bucket->getId())
->setPayload($bucket->getArrayCopy())
;
$events
->setParam('bucketId', $bucket->getId())
;
$usage->setParam('storage.buckets.create', 1);
@ -213,7 +218,7 @@ App::put('/v1/storage/buckets/:bucketId')
->desc('Update Bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'storage.buckets.update')
->label('event', 'buckets.[bucketId].update')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateBucket')
@ -235,11 +240,13 @@ App::put('/v1/storage/buckets/:bucketId')
->inject('dbForProject')
->inject('audits')
->inject('usage')
->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage) {
->inject('events')
->action(function ($bucketId, $name, $permission, $read, $write, $enabled, $maximumFileSize, $allowedFileExtensions, $encryption, $antivirus, $response, $dbForProject, $audits, $usage, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
$bucket = $dbForProject->getDocument('buckets', $bucketId);
@ -247,13 +254,13 @@ App::put('/v1/storage/buckets/:bucketId')
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
}
$read??=$bucket->getAttribute('$read', []); // By default inherit read permissions
$write??=$bucket->getAttribute('$write', []); // By default inherit write permissions
$maximumFileSize??=$bucket->getAttribute('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0));
$allowedFileExtensions??=$bucket->getAttribute('allowedFileExtensions', []);
$enabled??=$bucket->getAttribute('enabled', true);
$encryption??=$bucket->getAttribute('encryption', true);
$antivirus??=$bucket->getAttribute('antivirus', true);
$read ??= $bucket->getAttribute('$read', []); // By default inherit read permissions
$write ??= $bucket->getAttribute('$write', []); // By default inherit write permissions
$maximumFileSize ??= $bucket->getAttribute('maximumFileSize', (int) App::getEnv('_APP_STORAGE_LIMIT', 0));
$allowedFileExtensions ??= $bucket->getAttribute('allowedFileExtensions', []);
$enabled ??= $bucket->getAttribute('enabled', true);
$encryption ??= $bucket->getAttribute('encryption', true);
$antivirus ??= $bucket->getAttribute('antivirus', true);
$bucket = $dbForProject->updateDocument('buckets', $bucket->getId(), $bucket
->setAttribute('name', $name)
@ -268,9 +275,12 @@ App::put('/v1/storage/buckets/:bucketId')
);
$audits
->setParam('event', 'storage.buckets.update')
->setParam('resource', 'storage/buckets/' . $bucket->getId())
->setParam('data', $bucket->getArrayCopy())
->setResource('storage/buckets/' . $bucket->getId())
->setPayload($bucket->getArrayCopy())
;
$events
->setParam('bucketId', $bucket->getId())
;
$usage->setParam('storage.buckets.update', 1);
@ -282,7 +292,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->desc('Delete Bucket')
->groups(['api', 'storage'])
->label('scope', 'buckets.write')
->label('event', 'storage.buckets.delete')
->label('event', 'buckets.[bucketId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteBucket')
@ -299,7 +309,7 @@ App::delete('/v1/storage/buckets/:bucketId')
->action(function ($bucketId, $response, $dbForProject, $audits, $deletes, $events, $usage) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
@ -315,18 +325,21 @@ App::delete('/v1/storage/buckets/:bucketId')
}
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $bucket)
->setParam('bucketId', $bucket->getId())
->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $bucket
])
;
$events
->setParam('eventData', $response->output($bucket, Response::MODEL_BUCKET))
->setParam('bucketId', $bucket->getId())
->setPayload($response->output($bucket, Response::MODEL_BUCKET))
;
$audits
->setParam('event', 'storage.buckets.delete')
->setParam('resource', 'storage/buckets/' . $bucket->getId())
->setParam('data', $bucket->getArrayCopy())
->setResource('storage/buckets/' . $bucket->getId())
->setPayload($bucket->getArrayCopy())
;
$usage->setParam('storage.buckets.delete', 1);
@ -339,7 +352,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->desc('Create File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'storage.files.create')
->label('event', 'buckets.[bucketId].files.[fileId].create')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'createFile')
@ -369,7 +382,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
@ -581,9 +594,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
'metadata' => $metadata,
]);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucket, $doc) {
return $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
});
$file = Authorization::skip(fn () => $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc));
} else {
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
}
@ -603,9 +614,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setAttribute('chunksUploaded', $chunksUploaded);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId, $file) {
return $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
});
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
} else {
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
}
@ -618,8 +627,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
$audits
->setParam('event', 'storage.files.create')
->setParam('resource', 'storage/files/' . $file->getId())
->setResource('storage/files/' . $file->getId())
;
$usage
@ -651,9 +659,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
'metadata' => $metadata,
]);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucket, $doc) {
return $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
});
$file = Authorization::skip(fn () => $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc));
} else {
$file = $dbForProject->createDocument('bucket_' . $bucket->getInternalId(), $doc);
}
@ -663,9 +669,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setAttribute('metadata', $metadata);
if ($permissionBucket) {
$file = Authorization::skip(function () use ($dbForProject, $bucket, $fileId, $file) {
return $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
});
$file = Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file));
} else {
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
}
@ -677,13 +681,16 @@ App::post('/v1/storage/buckets/:bucketId/files')
}
}
$events->setParam('bucket', $bucket->getArrayCopy());
$events
->setParam('bucketId', $bucket->getId())
->setParam('fileId', $file->getId())
->setTrigger($bucket)
;
$metadata = null; // was causing leaks as it was passed by reference
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($file, Response::MODEL_FILE);
;
});
App::get('/v1/storage/buckets/:bucketId/files')
@ -1323,7 +1330,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
->desc('Update File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'storage.files.update')
->label('event', 'buckets.[bucketId].files.[fileId].update')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'updateFile')
@ -1346,7 +1353,7 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Document $user */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $events */
/** @var string $mode */
@ -1405,13 +1412,14 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
}
$events->setParam('bucket', $bucket->getArrayCopy());
$audits
->setParam('event', 'storage.files.update')
->setParam('resource', 'file/' . $file->getId())
$events
->setParam('bucketId', $bucket->getId())
->setParam('fileId', $file->getId())
->setTrigger($bucket)
;
$audits->setResource('file/' . $file->getId());
$usage
->setParam('storage.files.update', 1)
->setParam('bucketId', $bucketId)
@ -1425,7 +1433,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
->desc('Delete File')
->groups(['api', 'storage'])
->label('scope', 'files.write')
->label('event', 'storage.files.delete')
->label('event', 'buckets.[bucketId].files.[fileId].delete')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'storage')
->label('sdk.method', 'deleteFile')
@ -1447,7 +1455,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Utopia\Storage\Device $deviceFiles */
/** @var string $mode */
@ -1505,10 +1513,7 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
throw new Exception('Failed to delete file from device', 500, Exception::GENERAL_SERVER_ERROR);
}
$audits
->setParam('event', 'storage.files.delete')
->setParam('resource', 'file/' . $file->getId())
;
$audits->setResource('file/' . $file->getId());
$usage
->setParam('storage', $file->getAttribute('size', 0) * -1)
@ -1517,8 +1522,10 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
;
$events
->setParam('eventData', $response->output($file, Response::MODEL_FILE))
->setParam('bucket', $bucket->getArrayCopy())
->setParam('bucketId', $bucket->getId())
->setParam('fileId', $file->getId())
->setTrigger($bucket)
->setPayload($response->output($file, Response::MODEL_FILE))
;
$response->noContent();

View file

@ -26,7 +26,7 @@ use Utopia\Validator\WhiteList;
App::post('/v1/teams')
->desc('Create Team')
->groups(['api', 'teams'])
->label('event', 'teams.create')
->label('event', 'teams.[teamId].create')
->label('scope', 'teams.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@ -85,6 +85,8 @@ App::post('/v1/teams')
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
}
$events->setParam('teamId', $team->getId());
if (!empty($user->getId())) {
$events->setParam('userId', $user->getId());
}
@ -169,7 +171,7 @@ App::get('/v1/teams/:teamId')
App::put('/v1/teams/:teamId')
->desc('Update Team')
->groups(['api', 'teams'])
->label('event', 'teams.update')
->label('event', 'teams.[teamId].update')
->label('scope', 'teams.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@ -182,9 +184,11 @@ App::put('/v1/teams/:teamId')
->param('name', null, new Text(128), 'New team name. Max length: 128 chars.')
->inject('response')
->inject('dbForProject')
->action(function ($teamId, $name, $response, $dbForProject) {
->inject('events')
->action(function ($teamId, $name, $response, $dbForProject, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
$team = $dbForProject->getDocument('teams', $teamId);
@ -197,13 +201,15 @@ App::put('/v1/teams/:teamId')
->setAttribute('search', implode(' ', [$teamId, $name]))
);
$events->setParam('teamId', $team->getId());
$response->dynamic($team, Response::MODEL_TEAM);
});
App::delete('/v1/teams/:teamId')
->desc('Delete Team')
->groups(['api', 'teams'])
->label('event', 'teams.delete')
->label('event', 'teams.[teamId].delete')
->label('scope', 'teams.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@ -244,12 +250,17 @@ App::delete('/v1/teams/:teamId')
}
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $team)
->setParam('teamId', $team->getId())
->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $team
])
;
$events
->setParam('eventData', $response->output($team, Response::MODEL_TEAM))
->setParam('teamId', $team->getId())
->setPayload($response->output($team, Response::MODEL_TEAM))
;
$response->noContent();
@ -258,7 +269,7 @@ App::delete('/v1/teams/:teamId')
App::post('/v1/teams/:teamId/memberships')
->desc('Create Team Membership')
->groups(['api', 'teams', 'auth'])
->label('event', 'teams.memberships.create')
->label('event', 'teams.[teamId].memberships.[membershipId].create')
->label('scope', 'teams.write')
->label('auth.type', 'invites')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
@ -281,13 +292,15 @@ App::post('/v1/teams/:teamId/memberships')
->inject('locale')
->inject('audits')
->inject('mails')
->action(function ($teamId, $email, $roles, $url, $name, $response, $project, $user, $dbForProject, $locale, $audits, $mails) {
->inject('events')
->action(function ($teamId, $email, $roles, $url, $name, $response, $project, $user, $dbForProject, $locale, $audits, $mails, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Event $mails */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Mail $mails */
/** @var Appwrite\Event\Event $events */
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
@ -398,24 +411,24 @@ App::post('/v1/teams/:teamId/memberships')
if (!$isPrivilegedUser && !$isAppUser) { // No need of confirmation when in admin or app mode
$mails
->setParam('event', 'teams.memberships.create')
->setParam('from', $project->getId())
->setParam('recipient', $email)
->setParam('name', $name)
->setParam('url', $url)
->setParam('locale', $locale->default)
->setParam('project', $project->getAttribute('name', ['[APP-NAME]']))
->setParam('owner', $user->getAttribute('name', ''))
->setParam('team', $team->getAttribute('name', '[TEAM-NAME]'))
->setParam('type', MAIL_TYPE_INVITATION)
->setType(MAIL_TYPE_INVITATION)
->setRecipient($email)
->setUrl($url)
->setName($name)
->setLocale($locale->default)
->setTeam($team)
->setUser($user)
->trigger()
;
}
$audits
->setParam('userId', $invitee->getId())
->setParam('event', 'teams.memberships.create')
->setParam('resource', 'team/'.$teamId)
->setResource('team/'.$teamId)
;
$events
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
@ -548,7 +561,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
App::patch('/v1/teams/:teamId/memberships/:membershipId')
->desc('Update Membership Roles')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.update')
->label('event', 'teams.[teamId].memberships.[membershipId].update')
->label('scope', 'teams.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@ -565,12 +578,14 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('user')
->inject('dbForProject')
->inject('audits')
->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForProject, $audits) {
->inject('events')
->action(function ($teamId, $membershipId, $roles, $request, $response, $user, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@ -612,10 +627,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
Authorization::skip(fn () => $dbForProject->updateDocument('users', $profile->getId(), $profile));
$audits
->setParam('userId', $user->getId())
->setParam('event', 'teams.memberships.update')
->setParam('resource', 'team/' . $teamId);
$audits->setResource('team/' . $teamId);
$events
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId());
$response->dynamic(
$membership
@ -628,7 +644,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->desc('Update Team Membership Status')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.update.status')
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
->label('scope', 'public')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@ -647,13 +663,15 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->inject('dbForProject')
->inject('geodb')
->inject('audits')
->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForProject, $geodb, $audits) {
->inject('events')
->action(function ($teamId, $membershipId, $userId, $secret, $request, $response, $user, $dbForProject, $geodb, $audits, $events) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var MaxMind\Db\Reader $geodb */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$protocol = $request->getProtocol();
@ -677,7 +695,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception('Secret key not valid', 401, Exception::TEAM_INVALID_SECRET);
}
if ($userId != $membership->getAttribute('userId')) {
if ($userId !== $membership->getAttribute('userId')) {
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
}
@ -737,10 +755,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
$audits
->setParam('userId', $user->getId())
->setParam('event', 'teams.memberships.update.status')
->setParam('resource', 'team/'.$teamId)
$audits->setResource('team/'.$teamId);
$events
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId())
;
if (!Config::getParam('domainVerification')) {
@ -763,7 +782,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
App::delete('/v1/teams/:teamId/memberships/:membershipId')
->desc('Delete Team Membership')
->groups(['api', 'teams'])
->label('event', 'teams.memberships.delete')
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
->label('scope', 'teams.write')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'teams')
@ -780,7 +799,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->action(function ($teamId, $membershipId, $response, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$membership = $dbForProject->getDocument('memberships', $membershipId);
@ -833,14 +852,12 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
}
$audits
->setParam('userId', $membership->getAttribute('userId'))
->setParam('event', 'teams.memberships.delete')
->setParam('resource', 'team/'.$teamId)
;
$audits->setResource('team/'.$teamId);
$events
->setParam('eventData', $response->output($membership, Response::MODEL_MEMBERSHIP))
->setParam('teamId', $team->getId())
->setParam('membershipId', $membership->getId())
->setPayload($response->output($membership, Response::MODEL_MEMBERSHIP))
;
$response->noContent();

View file

@ -466,7 +466,7 @@ App::patch('/v1/users/:userId/name')
->action(function ($userId, $name, $response, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$user = $dbForProject->getDocument('users', $userId);
@ -478,9 +478,7 @@ App::patch('/v1/users/:userId/name')
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('name', $name));
$audits
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
->setResource('user/'.$user->getId())
;
$events
@ -511,7 +509,7 @@ App::patch('/v1/users/:userId/password')
->action(function ($userId, $password, $response, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$user = $dbForProject->getDocument('users', $userId);
@ -527,9 +525,7 @@ App::patch('/v1/users/:userId/password')
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
$audits
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
->setResource('user/'.$user->getId())
;
$events
@ -560,7 +556,7 @@ App::patch('/v1/users/:userId/email')
->action(function ($userId, $email, $response, $dbForProject, $audits, $events) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Event $events */
$user = $dbForProject->getDocument('users', $userId);
@ -584,9 +580,7 @@ App::patch('/v1/users/:userId/email')
$audits
->setPayload(array_merge($audits->getPayload(), [
'resource' => 'user/'.$user->getId()
]))
->setResource('user/'.$user->getId())
;
$events
@ -690,6 +684,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
$events
->setParam('userId', $user->getId())
->setParam('sessionId', $sessionId)
;
$response->noContent();
@ -794,8 +789,11 @@ App::delete('/v1/users/:userId')
$dbForProject->updateDocument('users', $userId, $user);
$deletes
->setParam('type', DELETE_TYPE_DOCUMENT)
->setParam('document', $clone)
->setParam('userId', $userId)
->setPayload([
'type' => DELETE_TYPE_DOCUMENT,
'document' => $clone
])
;
$events

View file

@ -12,6 +12,7 @@ use Appwrite\Extend\Exception;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Response\Filters\V11 as ResponseV11;
use Appwrite\Utopia\Response\Filters\V12 as ResponseV12;
@ -90,12 +91,10 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
Console::info('Issuing a TLS certificate for the master domain (' . $domain->get() . ') in a few seconds...');
Resque::enqueue('v1-certificates', 'CertificatesV1', [
'document' => $domainDocument,
'domain' => $domain->get(),
'validateTarget' => false,
'validateCNAME' => false,
]);
$event = new Certificate();
$event
->setDomain($domainDocument)
->trigger();
}
$domains[$domain->get()] = true;

View file

@ -7,14 +7,16 @@ use Utopia\App;
use Appwrite\Extend\Exception;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\TimeLimit;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Database\Validator\Authorization;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\S3;
use Utopia\Storage\Storage;
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $usage, $deletes, $database, $dbForProject, $mode) {
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $mails, $usage, $deletes, $database, $dbForProject, $mode) {
/** @var Utopia\App $utopia */
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
@ -22,7 +24,8 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
/** @var Utopia\Database\Document $user */
/** @var Utopia\Registry\Registry $register */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Event\Mail $mails */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $database */
@ -94,28 +97,18 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
->setUser($user)
;
$events
->setParam('projectId', $project->getId())
->setParam('webhooks', $project->getAttribute('webhooks', []))
->setParam('userId', $user->getId())
->setParam('event', $route->getLabel('event', ''))
->setParam('eventData', [])
->setParam('functionId', null)
->setParam('executionId', null)
->setParam('trigger', 'event')
$mails
->setProject($project)
->setUser($user)
;
$audits
->setMode($mode)
->setUserAgent($request->getUserAgent(''))
->setIP($request->getIP())
->setEvent($route->getLabel('event', ''))
->setProject($project)
->setUser($user)
->setPayload([
'mode' => $mode,
'userAgent' => $request->getUserAgent(''),
'ip' => $request->getIP(),
'data' => [],
'resource' => ''
])
;
$usage
@ -128,15 +121,10 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
->setParam('networkResponseSize', 0)
->setParam('storage', 0)
;
$deletes
->setParam('projectId', $project->getId())
;
$database
->setParam('projectId', $project->getId())
;
}, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'usage', 'deletes', 'database', 'dbForProject', 'mode'], 'api');
$deletes->setProject($project);
$database->setProject($project);
}, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'mails', 'usage', 'deletes', 'database', 'dbForProject', 'mode'], 'api');
App::init(function ($utopia, $request, $project) {
/** @var Utopia\App $utopia */
@ -191,56 +179,53 @@ App::init(function ($utopia, $request, $project) {
}, ['utopia', 'request', 'project'], 'auth');
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $database, $mode) {
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $database, $mode, $dbForProject) {
/** @var Utopia\App $utopia */
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
/** @var Appwrite\Event\Audit $audits */
/** @var Appwrite\Stats\Stats $usage */
/** @var Appwrite\Event\Event $deletes */
/** @var Appwrite\Event\Event $database */
/** @var Appwrite\Event\Database $database */
/** @var bool $mode */
/** @var Utopia\Database\Database $dbForProject */
if (!empty($events->getEvent())) {
$allEvents = Event::generateEvents($events->getEvent(), $events->getParams());
var_dump($request->getRoute()->getPath(), $events->getEvent(), $allEvents);
foreach ($project->getAttribute('webhooks', []) as $webhook) {
/**
* @var Document $webhook
*/
if (array_intersect($webhook->getAttribute('events', []), $allEvents)) {
$events
->setClass(Event::WEBHOOK_CLASS_NAME)
->setQueue(Event::WEBHOOK_QUEUE_NAME)
->setTrigger($webhook)
->setPayload($response->getPayload())
->trigger();
}
}
}
if (!empty($events->getParam('event'))) {
if (empty($events->getParam('eventData'))) {
$events->setParam('eventData', $response->getPayload());
}
$functions = clone $events;
$functions
->setQueue('v1-functions')
->setClass('FunctionsV1')
/**
* Trigger functions.
*/
$events
->setClass(Event::FUNCTIONS_CLASS_NAME)
->setQueue(Event::FUNCTIONS_QUEUE_NAME)
->setPayload($response->getPayload())
->trigger();
/**
* Trigger webhooks.
*/
$events
->setClass(Event::WEBHOOK_CLASS_NAME)
->setQueue(Event::WEBHOOK_QUEUE_NAME)
->setPayload($response->getPayload())
->trigger();
/**
* Trigger realtime.
*/
if ($project->getId() !== 'console') {
$allEvents = Event::generateEvents($events->getEvent(), $events->getParams());
$payload = new Document($response->getPayload());
$collection = new Document($events->getParam('collection') ?? []);
$bucket = new Document($events->getParam('bucket') ?? []);
$trigger = $events->getTrigger() ?? false;
$collection = ($trigger && $trigger->getCollection() === 'collections') ? $trigger : null;
$bucket = ($trigger && $trigger->getCollection() === 'buckets') ? $trigger : null;
$target = Realtime::fromPayload(
event: $events->getParam('event'),
payload: $payload,
project: $project,
event: $allEvents[0],
payload: $payload,
project: $project,
collection: $collection,
bucket: $bucket,
);
@ -248,7 +233,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
Realtime::send(
$target['projectId'] ?? $project->getId(),
$response->getPayload(),
$events->getParam('event'),
$allEvents[0],
$target['channels'],
$target['roles'],
[
@ -259,15 +244,18 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
}
}
if (!empty($audits->getEvent())) {
if (!empty($audits->getResource())) {
foreach ($events->getParams() as $key => $value) {
$audits->setParam($key, $value);
}
$audits->trigger();
}
if (!empty($deletes->getParam('type')) && !empty($deletes->getParam('document'))) {
if (!empty($deletes->getPayload())) {
$deletes->trigger();
}
if (!empty($database->getParam('type')) && !empty($database->getParam('document'))) {
if (!empty($database->getType())) {
$database->trigger();
}
@ -277,11 +265,11 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
&& $mode !== APP_MODE_ADMIN // TODO: add check to make sure user is admin
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
$usage
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
->setParam('networkResponseSize', $response->getSize())
->submit()
;
// $usage
// ->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
// ->setParam('networkResponseSize', $response->getSize())
// ->submit()
// ;
}
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode'], 'api');
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode', 'dbForProject'], 'api');

View file

@ -22,7 +22,11 @@ use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Extend\Exception;
use Appwrite\Auth\Auth;
use Appwrite\Event\Audit;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Mail;
use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\URL;
@ -604,10 +608,10 @@ App::setResource('locale', fn() => new Locale(App::getEnv('_APP_LOCALE', 'en')))
// Queues
App::setResource('events', fn() => new Event('', ''));
App::setResource('audits', fn() => new Event(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME));
App::setResource('mails', fn() => new Event(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME));
App::setResource('deletes', fn() => new Event(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME));
App::setResource('database', fn() => new Event(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME));
App::setResource('audits', fn() => new Audit());
App::setResource('mails', fn() => new Mail());
App::setResource('deletes', fn() => new Delete());
App::setResource('database', fn() => new EventDatabase());
App::setResource('usage', function($register) {
return new Stats($register->get('statsd'));
}, ['register']);

View file

@ -11,46 +11,56 @@ $cli
->desc('Schedules maintenance tasks and publishes them to resque')
->action(function () {
Console::title('Maintenance V1');
Console::success(APP_NAME.' maintenance process v1 has started');
Console::success(APP_NAME . ' maintenance process v1 has started');
function notifyDeleteExecutionLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_EXECUTIONS,
'timestamp' => time() - $interval
'payload' => [
'type' => DELETE_TYPE_EXECUTIONS,
'timestamp' => time() - $interval
]
]);
}
function notifyDeleteAbuseLogs(int $interval)
function notifyDeleteAbuseLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_ABUSE,
'timestamp' => time() - $interval
'payload' => [
'type' => DELETE_TYPE_ABUSE,
'timestamp' => time() - $interval
]
]);
}
function notifyDeleteAuditLogs(int $interval)
function notifyDeleteAuditLogs(int $interval)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_AUDIT,
'timestamp' => time() - $interval
'payload' => [
'type' => DELETE_TYPE_AUDIT,
'timestamp' => time() - $interval
]
]);
}
function notifyDeleteUsageStats(int $interval30m, int $interval1d)
function notifyDeleteUsageStats(int $interval30m, int $interval1d)
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_USAGE,
'timestamp1d' => time() - $interval1d,
'timestamp30m' => time() - $interval30m,
'payload' => [
'type' => DELETE_TYPE_USAGE,
'timestamp1d' => time() - $interval1d,
'timestamp30m' => time() - $interval30m,
]
]);
}
function notifyDeleteConnections()
function notifyDeleteConnections()
{
Resque::enqueue(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME, [
'type' => DELETE_TYPE_REALTIME,
'timestamp' => time() - 60
'payload' => [
'type' => DELETE_TYPE_REALTIME,
'timestamp' => time() - 60
]
]);
}
@ -59,10 +69,10 @@ $cli
$executionLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_EXECUTION', '1209600');
$auditLogRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', '1209600');
$abuseLogsRetention = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', '86400');
$usageStatsRetention30m = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_30M', '129600');//36 hours
$usageStatsRetention30m = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_30M', '129600'); //36 hours
$usageStatsRetention1d = (int) App::getEnv('_APP_MAINTENANCE_RETENTION_USAGE_1D', '8640000'); // 100 days
Console::loop(function() use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) {
Console::loop(function () use ($interval, $executionLogsRetention, $abuseLogsRetention, $auditLogRetention, $usageStatsRetention30m, $usageStatsRetention1d) {
$time = date('d-m-Y H:i:s', time());
Console::info("[{$time}] Notifying deletes workers every {$interval} seconds");
notifyDeleteExecutionLogs($executionLogsRetention);
@ -71,4 +81,4 @@ $cli
notifyDeleteUsageStats($usageStatsRetention30m, $usageStatsRetention1d);
notifyDeleteConnections();
}, $interval);
});
});

View file

@ -2,8 +2,10 @@
global $cli;
use Appwrite\Event\Certificate;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Database\Document;
$cli
->task('ssl')
@ -11,13 +13,13 @@ $cli
->action(function () {
$domain = App::getEnv('_APP_DOMAIN', '');
Console::log('Issue a TLS certificate for master domain ('.$domain.') in 30 seconds.
Console::log('Issue a TLS certificate for master domain (' . $domain . ') in 30 seconds.
Make sure your domain points to your server or restart to try again.');
ResqueScheduler::enqueueAt(\time() + 30, 'v1-certificates', 'CertificatesV1', [
'document' => [],
'domain' => $domain,
'validateTarget' => false,
'validateCNAME' => false,
]);
});
$event = new Certificate();
$event
->setDomain(new Document([
'domain' => $domain
]))
->trigger();
});

View file

@ -24,19 +24,19 @@ class AuditsV1 extends Worker
public function run(): void
{
$events = $this->args['events'];
$payload = $this->args['payload'];
$mode = $this->args['mode'];
$resource = $this->args['resource'];
$userAgent = $this->args['userAgent'];
$ip = $this->args['ip'];
$user = new Document($this->args['user']);
$project = new Document($this->args['project']);
$payload = $this->args['payload'];
$userName = $user->getAttribute('name', '');
$userEmail = $user->getAttribute('email', '');
$event = $events[0];
$mode = $payload['mode'];
$resource = $payload['resource'];
$userAgent = $payload['userAgent'];
$ip = $payload['ip'];
$data = $payload['data'];
$dbForProject = $this->getProjectDB($project->getId());
$audit = new Audit($dbForProject);
@ -44,12 +44,11 @@ class AuditsV1 extends Worker
'userName' => $userName,
'userEmail' => $userEmail,
'mode' => $mode,
'data' => $data,
'data' => $payload,
]);
}
public function shutdown(): void
{
// ... Remove environment for this job
}
}

View file

@ -21,7 +21,7 @@ Console::success(APP_NAME.' build worker v1 has started');
// TODO: Executor should return appropriate response codes.
class BuildsV1 extends Worker
{
{
/**
* @var Executor
*/
@ -42,7 +42,7 @@ class BuildsV1 extends Worker
$projectId = $this->args['projectId'] ?? '';
$functionId = $this->args['resourceId'] ?? '';
$deploymentId = $this->args['deploymentId'] ?? '';
switch ($type) {
case BUILD_TYPE_DEPLOYMENT:
case BUILD_TYPE_RETRY:
@ -61,7 +61,7 @@ class BuildsV1 extends Worker
$dbForProject = $this->getProjectDB($projectId);
$dbForConsole = $this->getConsoleDB();
$project = $dbForConsole->getDocument('projects', $projectId);
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception('Function not found', 404);
@ -177,7 +177,7 @@ class BuildsV1 extends Worker
$build = $dbForProject->updateDocument('builds', $buildId, $build);
/**
* Send realtime Event
* Send realtime Event
*/
$target = Realtime::fromPayload('functions.deployments.update', $build, $project);
Realtime::send(

View file

@ -42,16 +42,12 @@ class CertificatesV1 extends Worker
Authorization::disable();
// Args
$document = $this->args['document'];
$domain = $this->args['domain'];
// Validation Args
$document = new Document($this->args['domain'] ?? []);
$validateTarget = $this->args['validateTarget'] ?? true;
$validateCNAME = $this->args['validateCNAME'] ?? true;
// Options
$domain = new Domain((!empty($domain)) ? $domain : '');
$domain = new Domain($document->getAttribute('domain'));
$expiry = 60 * 60 * 24 * 30 * 2; // 60 days
$safety = 60 * 60; // 1 hour
$renew = (\time() + $expiry);
@ -158,11 +154,10 @@ class CertificatesV1 extends Worker
throw new Exception('Failed saving certificate to DB');
}
if(!empty($document)) {
$certificate = new Document(\array_merge($document, [
'updated' => \time(),
'certificateId' => $certificate->getId(),
]));
if($document->getId()) {
$document
->setAttribute('updated', \time())
->setAttribute('certificateId', $certificate->getId());
$certificate = $dbForConsole->updateDocument('domains', $certificate->getId(), $certificate);

View file

@ -20,12 +20,11 @@ class DatabaseV1 extends Worker
public function run(): void
{
Authorization::disable();
$projectId = $this->args['projectId'] ?? '';
$type = $this->args['type'] ?? '';
$collection = $this->args['collection'] ?? [];
$collection = new Document($collection);
$document = $this->args['document'] ?? [];
$document = new Document($document);
$type = $this->args['type'];
$project = new Document($this->args['project']);
$collection = new Document($this->args['collection'] ?? []);
$document = new Document($this->args['document'] ?? []);
if($collection->isEmpty()) {
throw new Exception('Missing collection');
@ -37,16 +36,16 @@ class DatabaseV1 extends Worker
switch (strval($type)) {
case DATABASE_TYPE_CREATE_ATTRIBUTE:
$this->createAttribute($collection, $document, $projectId);
$this->createAttribute($collection, $document, $project->getId());
break;
case DATABASE_TYPE_DELETE_ATTRIBUTE:
$this->deleteAttribute($collection, $document, $projectId);
$this->deleteAttribute($collection, $document, $project->getId());
break;
case DATABASE_TYPE_CREATE_INDEX:
$this->createIndex($collection, $document, $projectId);
$this->createIndex($collection, $document, $project->getId());
break;
case DATABASE_TYPE_DELETE_INDEX:
$this->deleteIndex($collection, $document, $projectId);
$this->deleteIndex($collection, $document, $project->getId());
break;
default:

View file

@ -19,6 +19,7 @@ use Utopia\Audit\Audit;
require_once __DIR__ . '/../init.php';
Authorization::disable();
Authorization::setDefaultStatus(false);
Console::title('Deletes V1 Worker');
Console::success(APP_NAME . ' deletes worker v1 has started' . "\n");
@ -40,34 +41,36 @@ class DeletesV1 extends Worker
public function run(): void
{
$projectId = $this->args['projectId'] ?? '';
$type = $this->args['type'] ?? '';
$project = new Document($this->args['project'] ?? []);
$payload = $this->args['payload'] ?? [];
$type = $payload['type'] ?? '';
switch (strval($type)) {
case DELETE_TYPE_DOCUMENT:
$document = new Document($this->args['document'] ?? []);
$document = new Document($payload['document'] ?? []);
switch ($document->getCollection()) {
case DELETE_TYPE_COLLECTIONS:
$this->deleteCollection($document, $projectId);
$this->deleteCollection($document, $project->getId());
break;
case DELETE_TYPE_PROJECTS:
$this->deleteProject($document);
break;
case DELETE_TYPE_FUNCTIONS:
$this->deleteFunction($document, $projectId);
$this->deleteFunction($document, $project->getId());
break;
case DELETE_TYPE_DEPLOYMENTS:
$this->deleteDeployment($document, $projectId);
$this->deleteDeployment($document, $project->getId());
break;
case DELETE_TYPE_USERS:
$this->deleteUser($document, $projectId);
$this->deleteUser($document, $project->getId());
break;
case DELETE_TYPE_TEAMS:
$this->deleteMemberships($document, $projectId);
$this->deleteMemberships($document, $project->getId());
break;
case DELETE_TYPE_BUCKETS:
$this->deleteBucket($document, $projectId);
$this->deleteBucket($document, $project->getId());
break;
default:
Console::error('No lazy delete operation available for document of type: ' . $document->getCollection());
@ -76,38 +79,38 @@ class DeletesV1 extends Worker
break;
case DELETE_TYPE_EXECUTIONS:
$this->deleteExecutionLogs($this->args['timestamp']);
$this->deleteExecutionLogs($payload['timestamp']);
break;
case DELETE_TYPE_AUDIT:
$timestamp = $this->args['timestamp'] ?? 0;
$document = new Document($this->args['document'] ?? []);
$timestamp = $payload['timestamp'] ?? 0;
$document = new Document($payload['document'] ?? []);
if (!empty($timestamp)) {
$this->deleteAuditLogs($this->args['timestamp']);
$this->deleteAuditLogs($payload['timestamp']);
}
if (!$document->isEmpty()) {
$this->deleteAuditLogsByResource('document/' . $document->getId(), $projectId);
$this->deleteAuditLogsByResource('document/' . $document->getId(), $project->getId());
}
break;
case DELETE_TYPE_ABUSE:
$this->deleteAbuseLogs($this->args['timestamp']);
$this->deleteAbuseLogs($payload['timestamp']);
break;
case DELETE_TYPE_REALTIME:
$this->deleteRealtimeUsage($this->args['timestamp']);
$this->deleteRealtimeUsage($payload['timestamp']);
break;
case DELETE_TYPE_CERTIFICATES:
$document = new Document($this->args['document']);
$document = new Document($payload['document']);
$this->deleteCertificates($document);
break;
case DELETE_TYPE_USAGE:
$this->deleteUsageStats($this->args['timestamp1d'], $this->args['timestamp30m']);
$this->deleteUsageStats($payload['timestamp1d'], $payload['timestamp30m']);
break;
default:
Console::error('No delete operation for type: ' . $type);
@ -205,7 +208,7 @@ class DeletesV1 extends Worker
* DO NOT DELETE THE USER RECORD ITSELF.
* WE RETAIN THE USER RECORD TO RESERVE THE USER ID AND ENSURE THAT THE USER ID IS NOT REUSED.
*/
$userId = $document->getId();
$user = $this->getProjectDB($projectId)->getDocument('users', $userId);
@ -213,9 +216,10 @@ class DeletesV1 extends Worker
$this->deleteByGroup('sessions', [
new Query('userId', Query::TYPE_EQUAL, [$userId])
], $this->getProjectDB($projectId));
$user->setAttribute('sessions', []);
$updated = $this->getProjectDB($projectId)->updateDocument('users', $userId, $user);
$this->getProjectDB($projectId)->updateDocument('users', $userId, $user);
// Delete Memberships and decrement team membership counts
$this->deleteByGroup('memberships', [

View file

@ -7,7 +7,6 @@ use Appwrite\Stats\Stats;
use Appwrite\Utopia\Response\Model\Execution;
use Cron\CronExpression;
use Executor\Executor;
use Swoole\Runtime;
use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
@ -15,23 +14,19 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
require_once __DIR__.'/../init.php';
require_once __DIR__ . '/../init.php';
Console::title('Functions V1 Worker');
Console::success(APP_NAME . ' functions worker v1 has started');
class FunctionsV1 extends Worker
{
/**
* @var Executor
*/
private $executor = null;
private ?Executor $executor = null;
public array $args = [];
public array $allowed = [];
public function getName(): string {
public function getName(): string
{
return "functions";
}
@ -42,19 +37,22 @@ class FunctionsV1 extends Worker
public function run(): void
{
$projectId = $this->args['projectId'] ?? '';
$functionId = $this->args['functionId'] ?? '';
$webhooks = $this->args['webhooks'] ?? [];
$executionId = $this->args['executionId'] ?? '';
$trigger = $this->args['trigger'] ?? '';
$event = $this->args['event'] ?? '';
$scheduleOriginal = $this->args['scheduleOriginal'] ?? '';
$eventData = (!empty($this->args['eventData'])) ? json_encode($this->args['eventData']) : '';
$data = $this->args['data'] ?? '';
$userId = $this->args['userId'] ?? '';
$jwt = $this->args['jwt'] ?? '';
$events = $this->args['events'];
$payload = $this->args['payload'];
$user = new Document($this->args['user'] ?? []);
$project = new Document($this->args['project'] ?? []);
$execution = new Document($this->args['trigger'] ?? []);
$database = $this->getProjectDB($projectId);
$event = $events[0] ?? '';
$data = $payload['data'] ?? '';
$jwt = $payload['jwt'] ?? '';
$webhooks = $project->getAttribute('webhooks', []);
$trigger = $execution->getAttribute('trigger', '');
$scheduleOriginal = $execution->getAttribute('scheduleOriginal', '');
$eventData = !empty($execution->getAttribute('eventData')) ? json_encode($execution->getAttribute('eventData')) : '';
$jwt = $execution->getAttribute('jwt', '');
$database = $this->getProjectDB($project->getId());
switch ($trigger) {
case 'event':
@ -72,25 +70,23 @@ class FunctionsV1 extends Worker
Console::log('Fetched ' . $sum . ' functions...');
foreach ($functions as $function) {
$events = $function->getAttribute('events', []);
if (!\in_array($event, $events)) {
if (!array_intersect($events, $function->getAttribute('events', []))) {
continue;
}
Console::success('Iterating function: ' . $function->getAttribute('name'));
$this->execute(
projectId: $projectId,
projectId: $project->getId(),
function: $function,
dbForProject: $database,
executionId: $executionId,
executionId: $execution->getId(),
webhooks: $webhooks,
trigger: $trigger,
event: $event,
eventData: $eventData,
data: $data,
userId: $userId,
userId: $user->getId(),
jwt: $jwt
);
@ -98,9 +94,29 @@ class FunctionsV1 extends Worker
}
}
break;
case 'http':
$function = Authorization::skip(fn() => $database->getDocument('functions', $execution->getAttribute('functionId')));
$this->execute(
projectId: $project->getId(),
function: $function,
dbForProject: $database,
executionId: $execution->getId(),
webhooks: $webhooks,
trigger: $trigger,
event: '',
eventData: '',
data: $data,
userId: $user->getId(),
jwt: $jwt
);
break;
case 'schedule':
$function = Authorization::skip(fn() => $database->getDocument('functions', $execution->getAttribute('functionId')));
/*
* 1. Get Original Task
* 2. Check for updates
@ -115,10 +131,8 @@ class FunctionsV1 extends Worker
*/
// Reschedule
$function = Authorization::skip(fn() => $database->getDocument('functions', $functionId));
if (empty($function->getId())) {
throw new Exception('Function not found ('.$functionId.')');
throw new Exception('Function not found (' . $function->getId() . ')');
}
if ($scheduleOriginal && $scheduleOriginal !== $function->getAttribute('schedule')) { // Schedule has changed from previous run, ignore this run.
@ -132,60 +146,39 @@ class FunctionsV1 extends Worker
->setAttribute('scheduleNext', $next)
->setAttribute('schedulePrevious', \time());
$function = Authorization::skip(function() use ($database, $function, $next, $functionId) {
$function = $database->updateDocument('functions', $function->getId(), new Document(array_merge($function->getArrayCopy(), [
$function = Authorization::skip(fn () => $database->updateDocument(
'functions',
$function->getId(),
new Document(array_merge($function->getArrayCopy(), [
'scheduleNext' => (int)$next,
])));
if ($function === false) {
throw new Exception('Function update failed (' . $functionId . ')');
}
return $function;
});
]))
));
if ($function === false) {
throw new Exception('Function update failed.');
}
ResqueScheduler::enqueueAt($next, Event::FUNCTIONS_QUEUE_NAME, Event::FUNCTIONS_CLASS_NAME, [
'projectId' => $projectId,
'projectId' => $project->getId(),
'webhooks' => $webhooks,
'functionId' => $function->getId(),
'userId' => $userId,
'userId' => $user->getId(),
'executionId' => null,
'trigger' => 'schedule',
'scheduleOriginal' => $function->getAttribute('schedule', ''),
]); // Async task reschedule
$this->execute(
projectId: $projectId,
projectId: $project->getId(),
function: $function,
dbForProject: $database,
executionId: $executionId,
executionId: $execution->getId(),
webhooks: $webhooks,
trigger: $trigger,
event: $event,
eventData: $eventData,
data: $data,
userId: $userId,
jwt: $jwt
);
break;
case 'http':
$function = Authorization::skip(fn() => $database->getDocument('functions', $functionId));
if (empty($function->getId())) {
throw new Exception('Function not found ('.$functionId.')');
}
$this->execute(
projectId: $projectId,
function: $function,
dbForProject: $database,
executionId: $executionId,
webhooks: $webhooks,
trigger: $trigger,
event: $event,
eventData: $eventData,
data: $data,
userId: $userId,
userId: $user->getId(),
jwt: $jwt
);
@ -200,7 +193,7 @@ class FunctionsV1 extends Worker
string $executionId,
array $webhooks,
string $trigger,
string $event,
string $event,
string $eventData,
string $data,
string $userId,
@ -211,7 +204,7 @@ class FunctionsV1 extends Worker
$deploymentId = $function->getAttribute('deployment', '');
/** Check if deployment exists */
$deployment = Authorization::skip(fn() => $dbForProject->getDocument('deployments', $deploymentId));
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $deploymentId));
if ($deployment->getAttribute('resourceId') !== $functionId) {
throw new Exception('Deployment not found. Create deployment before trying to execute a function', 404);
@ -222,7 +215,7 @@ class FunctionsV1 extends Worker
}
/** Check if build has exists */
$build = Authorization::skip(fn() => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
throw new Exception('Build not found', 404);
}
@ -233,14 +226,15 @@ class FunctionsV1 extends Worker
/** Check if runtime is supported */
$runtimes = Config::getParam('runtimes', []);
$runtime = (isset($runtimes[$function->getAttribute('runtime', '')])) ? $runtimes[$function->getAttribute('runtime', '')] : null;
if (\is_null($runtime)) {
if (!\array_key_exists($function->getAttribute('runtime'), $runtimes)) {
throw new Exception('Runtime "' . $function->getAttribute('runtime', '') . '" is not supported', 400);
}
$runtime = $runtimes[$function->getAttribute('runtime')];
/** Create execution or update execution status */
$execution = Authorization::skip(function() use ($dbForProject, &$executionId, $functionId, $deploymentId, $trigger, $userId) {
$execution = Authorization::skip(function () use ($dbForProject, &$executionId, $functionId, $deploymentId, $trigger, $userId) {
$execution = $dbForProject->getDocument('executions', $executionId);
if ($execution->isEmpty()) {
$executionId = $dbForProject->getId();
@ -259,13 +253,14 @@ class FunctionsV1 extends Worker
'time' => 0.0,
'search' => implode(' ', [$functionId, $executionId]),
]));
if ($execution->isEmpty()) {
throw new Exception('Failed to create or read execution');
}
}
$execution->setAttribute('status', 'processing');
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
return $execution;
});
@ -301,19 +296,21 @@ class FunctionsV1 extends Worker
);
/** Update execution status */
$execution->setAttribute('status', $executionResponse['status']);
$execution->setAttribute('statusCode', $executionResponse['statusCode']);
$execution->setAttribute('stdout', $executionResponse['stdout']);
$execution->setAttribute('stderr', $executionResponse['stderr']);
$execution->setAttribute('time', $executionResponse['time']);
$execution
->setAttribute('status', $executionResponse['status'])
->setAttribute('statusCode', $executionResponse['statusCode'])
->setAttribute('stdout', $executionResponse['stdout'])
->setAttribute('stderr', $executionResponse['stderr'])
->setAttribute('time', $executionResponse['time']);
} catch (\Throwable $th) {
$execution->setAttribute('status', 'failed');
$execution->setAttribute('statusCode', $th->getCode());
$execution->setAttribute('stderr', $th->getMessage());
$execution
->setAttribute('status', 'failed')
->setAttribute('statusCode', $th->getCode())
->setAttribute('stderr', $th->getMessage());
Console::error($th->getMessage());
}
$execution = Authorization::skip(fn() => $dbForProject->updateDocument('executions', $executionId, $execution));
$execution = Authorization::skip(fn () => $dbForProject->updateDocument('executions', $executionId, $execution));
/** Trigger Webhook */
$executionModel = new Execution();
@ -323,8 +320,8 @@ class FunctionsV1 extends Worker
->setParam('userId', $userId)
->setParam('webhooks', $webhooks)
->setParam('event', 'functions.executions.update')
->setParam('eventData', $execution->getArrayCopy(array_keys($executionModel->getRules())));
$executionUpdate->trigger();
->setParam('eventData', $execution->getArrayCopy(array_keys($executionModel->getRules())))
->trigger();
/** Trigger realtime event */
$target = Realtime::fromPayload('functions.executions.update', $execution);
@ -357,7 +354,6 @@ class FunctionsV1 extends Worker
->setParam('networkRequestSize', 0)
->setParam('networkResponseSize', 0)
->submit();
$usage->submit();
}
}

View file

@ -31,28 +31,30 @@ class MailsV1 extends Worker
return;
}
$payload = $this->args['payload'];
$project = new Document($this->args['project']);
$user = new Document($this->args['user'] ?? []);
$team = new Document($this->args['team'] ?? []);
$recipient = $payload['recipient'];
$url = $payload['url'];
$project = $payload['project'];
$type = $payload['type'];
$name = $payload['name'] ?? $recipient;
$recipient = $this->args['recipient'];
$url = $this->args['url'];
$name = $this->args['name'];
$type = $this->args['type'];
$prefix = $this->getPrefix($type);
$locale = new Locale($payload['locale']);
$locale = new Locale($this->args['locale']);
$projectName = $project->getAttribute('name', ['[APP-NAME]']);
if (!$this->doesLocaleExist($locale, $prefix)) {
$locale->setDefault('en');
}
$from = $payload['from'] === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $project);
$from = $project->getId() === 'console' ? '' : \sprintf($locale->getText('emails.sender'), $projectName);
$body = Template::fromFile(__DIR__ . '/../config/locale/templates/email-base.tpl');
$subject = '';
switch ($type) {
case MAIL_TYPE_INVITATION:
$subject = \sprintf($locale->getText("$prefix.subject"), $payload['team'], $project);
$body->setParam('{{owner}}', $payload['owner']);
$body->setParam('{{team}}', $payload['team']);
$subject = \sprintf($locale->getText("$prefix.subject"), $team->getAttribute('name'), $projectName);
$body->setParam('{{owner}}', $user->getAttribute('name'));
$body->setParam('{{team}}', $team->getAttribute('name'));
break;
case MAIL_TYPE_RECOVERY:
case MAIL_TYPE_VERIFICATION:
@ -72,7 +74,7 @@ class MailsV1 extends Worker
->setParam('{{footer}}', $locale->getText("$prefix.footer"))
->setParam('{{thanks}}', $locale->getText("$prefix.thanks"))
->setParam('{{signature}}', $locale->getText("$prefix.signature"))
->setParam('{{project}}', $project)
->setParam('{{project}}', $projectName)
->setParam('{{direction}}', $locale->getText('settings.direction'))
->setParam('{{bg-body}}', '#f7f7f7')
->setParam('{{bg-content}}', '#ffffff')

View file

@ -12,6 +12,8 @@ Console::success(APP_NAME . ' webhooks worker v1 has started');
class WebhooksV1 extends Worker
{
protected array $errors = [];
public function getName(): string
{
return "webhooks";
@ -23,15 +25,24 @@ class WebhooksV1 extends Worker
public function run(): void
{
$errors = [];
// Event
$events = $this->args['events'];
$user = new Document($this->args['user']);
$payload = json_encode($this->args['payload']);
$project = new Document($this->args['project']);
$webhook = new Document($this->args['trigger'] ?? []);
$payload = \json_encode($this->args['payload']);
$user = new Document($this->args['user'] ?? []);
foreach ($project->getAttribute('webhooks', []) as $webhook) {
if (array_intersect($webhook->getAttribute('events', []), $events)) {
$this->execute($events, $payload, $webhook, $user, $project);
}
}
if (!empty($this->errors)) {
throw new Exception(\implode(" / \n\n", $this->errors));
}
}
protected function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void
{
$httpUser = $webhook->getAttribute('httpUser');
$httpPass = $webhook->getAttribute('httpPass');
@ -72,17 +83,14 @@ class WebhooksV1 extends Worker
}
if (false === \curl_exec($ch)) {
$errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name');
$this->errors[] = \curl_error($ch) . ' in events ' . implode(', ', $events) . ' for webhook ' . $webhook->getAttribute('name');
}
\curl_close($ch);
if (!empty($errors)) {
throw new Exception(\implode(" / \n\n", $errors));
}
}
public function shutdown(): void
{
$this->errors = [];
}
}

View file

@ -0,0 +1,81 @@
<?php
namespace Appwrite\Event;
use Resque;
class Audit extends Event
{
protected string $resource = '';
protected string $mode = '';
protected string $userAgent = '';
protected string $ip = '';
public function __construct()
{
parent::__construct(Event::AUDITS_QUEUE_NAME, Event::AUDITS_CLASS_NAME);
}
public function setResource(string $resource): self
{
$this->resource = $resource;
return $this;
}
public function getResource(): string
{
return $this->resource;
}
public function setMode(string $mode): self
{
$this->mode = $mode;
return $this;
}
public function getMode(): string
{
return $this->mode;
}
public function setUserAgent(string $userAgent): self
{
$this->userAgent = $userAgent;
return $this;
}
public function getUserAgent(): string
{
return $this->userAgent;
}
public function setIP(string $ip): self
{
$this->ip = $ip;
return $this;
}
public function getIP(): string
{
return $this->ip;
}
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'payload' => $this->payload,
'trigger' => $this->trigger,
'resource' => $this->resource,
'mode' => $this->mode,
'ip' => $this->ip,
'userAgent' => $this->userAgent,
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
]);
}
}

View file

@ -0,0 +1,64 @@
<?php
namespace Appwrite\Event;
use Resque;
use Utopia\Database\Document;
class Certificate extends Event
{
protected ?Document $domain = null;
protected bool $validateTarget = false;
protected bool $validateCNAME = false;
public function __construct()
{
parent::__construct(Event::CERTIFICATES_QUEUE_NAME, Event::CERTIFICATES_CLASS_NAME);
}
public function setDomain(Document $domain): self
{
$this->domain = $domain;
return $this;
}
public function getDomain(): ?Document
{
return $this->domain;
}
public function setValidateTarget(bool $validateTarget): self
{
$this->validateTarget = $validateTarget;
return $this;
}
public function getValidateTarget(): bool
{
return $this->validateTarget;
}
public function setValidateCNAME(bool $validateCNAME): self
{
$this->validateCNAME = $validateCNAME;
return $this;
}
public function getValidateCNAME(): bool
{
return $this->validateCNAME;
}
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'domain' => $this->domain,
'validateTarget' => $this->validateTarget,
'validateCNAME' => $this->validateCNAME
]);
}
}

View file

@ -0,0 +1,66 @@
<?php
namespace Appwrite\Event;
use Resque;
use Utopia\Database\Document;
class Database extends Event
{
protected string $type = '';
protected ?Document $collection = null;
protected ?Document $document = null;
public function __construct()
{
parent::__construct(Event::DATABASE_QUEUE_NAME, Event::DATABASE_CLASS_NAME);
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getType(): string
{
return $this->type;
}
public function setCollection(Document $collection): self
{
$this->collection = $collection;
return $this;
}
public function getCollection(): Document
{
return $this->collection;
}
public function setDocument(Document $document): self
{
$this->document = $document;
return $this;
}
public function getDocument(): Document
{
return $this->document;
}
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'type' => $this->type,
'collection' => $this->collection,
'document' => $this->document,
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
]);
}
}

View file

@ -0,0 +1,50 @@
<?php
namespace Appwrite\Event;
use Resque;
use Utopia\Database\Document;
class Delete extends Event
{
protected string $type = '';
protected ?Document $document = null;
public function __construct()
{
parent::__construct(Event::DELETE_QUEUE_NAME, Event::DELETE_CLASS_NAME);
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getType(): string
{
return $this->type;
}
public function setDocument(Document $document): self
{
$this->document = $document;
return $this;
}
public function getDocument(): Document
{
return $this->document;
}
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'type' => $this->type,
'document' => $this->document,
]);
}
}

View file

@ -103,26 +103,26 @@ class Event
public function setProject(Document $project): self
{
$this->projectId = $project;
$this->project = $project;
return $this;
}
public function getProjectId(): Document
public function getProject(): Document
{
return $this->projectId;
return $this->project;
}
public function setUser(Document $user): self
{
$this->userId = $user;
$this->user = $user;
return $this;
}
public function getUserId(): Document
public function getUser(): Document
{
return $this->userId;
return $this->user;
}
public function setPayload(array $payload): self
@ -144,7 +144,7 @@ class Event
return $this;
}
public function getTrigger(): Document
public function getTrigger(): ?Document
{
return $this->trigger;
}
@ -215,8 +215,8 @@ class Event
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->projectId,
'user' => $this->userId,
'project' => $this->project,
'user' => $this->user,
'payload' => $this->payload,
'trigger' => $this->trigger,
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
@ -294,11 +294,11 @@ class Event
$action = $parsed['action'];
$attribute = $parsed['attribute'];
if ($resource && !\in_array(\trim($resource, '[]'), $paramKeys)) {
if ($resource && !\in_array(\trim($resource, "\[\]"), $paramKeys)) {
throw new InvalidArgumentException("{$resource} is missing from the params.");
}
if ($subResource && !\in_array(\trim($subResource, '[]'), $paramKeys)) {
if ($subResource && !\in_array(\trim($subResource, "\[\]"), $paramKeys)) {
throw new InvalidArgumentException("{$subResource} is missing from the params.");
}

110
src/Appwrite/Event/Mail.php Normal file
View file

@ -0,0 +1,110 @@
<?php
namespace Appwrite\Event;
use Resque;
use Utopia\Database\Document;
class Mail extends Event
{
protected string $recipient = '';
protected string $url = '';
protected string $type = '';
protected string $name = '';
protected string $locale = '';
protected ?Document $team = null;
public function __construct()
{
parent::__construct(Event::MAILS_QUEUE_NAME, Event::MAILS_CLASS_NAME);
}
public function setTeam(Document $team): self
{
$this->team = $team;
return $this;
}
public function getTeam(): Document
{
return $this->team;
}
public function setRecipient(string $recipient): self
{
$this->recipient = $recipient;
return $this;
}
public function getRecipient(): string
{
return $this->recipient;
}
public function setUrl(string $url): self
{
$this->url = $url;
return $this;
}
public function getURL(): string
{
return $this->url;
}
public function setType(string $type): self
{
$this->type = $type;
return $this;
}
public function getType(): string
{
return $this->type;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function getName(): string
{
return $this->name;
}
public function setLocale(string $locale): self
{
$this->locale = $locale;
return $this;
}
public function getLocale(): string
{
return $this->locale;
}
public function trigger(): string|bool
{
return Resque::enqueue($this->queue, $this->class, [
'project' => $this->project,
'user' => $this->user,
'payload' => $this->payload,
'trigger' => $this->trigger,
'recipient' => $this->recipient,
'url' => $this->url,
'locale' => $this->locale,
'type' => $this->type,
'name' => $this->name,
'team' => $this->team,
'events' => Event::generateEvents($this->getEvent(), $this->getParams())
]);
}
}

View file

@ -246,77 +246,72 @@ class Realtime extends Adapter
$roles = [];
$permissionsChanged = false;
$projectId = null;
$parts = explode('.', $event);
switch (true) {
case strpos($event, 'account.recovery.') === 0:
case strpos($event, 'account.sessions.') === 0:
case strpos($event, 'account.verification.') === 0:
switch ($parts[0]) {
case 'users':
$channels[] = 'account';
$channels[] = 'account.' . $payload->getAttribute('userId');
$roles = ['user:' . $payload->getAttribute('userId')];
$channels[] = 'account.' . $parts[1];
$roles = ['user:' . $parts[1]];
break;
case strpos($event, 'account.') === 0:
$channels[] = 'account';
$channels[] = 'account.' . $payload->getId();
$roles = ['user:' . $payload->getId()];
break;
case strpos($event, 'teams.memberships') === 0:
$permissionsChanged = in_array($event, ['teams.memberships.update', 'teams.memberships.delete', 'teams.memberships.update.status']);
$channels[] = 'memberships';
$channels[] = 'memberships.' . $payload->getId();
$roles = ['team:' . $payload->getAttribute('teamId')];
break;
case strpos($event, 'teams.') === 0:
$permissionsChanged = $event === 'teams.create';
$channels[] = 'teams';
$channels[] = 'teams.' . $payload->getId();
$roles = ['team:' . $payload->getId()];
break;
case strpos($event, 'database.attributes.') === 0:
case strpos($event, 'database.indexes.') === 0:
$channels[] = 'console';
$projectId = 'console';
$roles = ['team:' . $project->getAttribute('teamId')];
break;
case strpos($event, 'database.documents.') === 0:
if ($collection->isEmpty()) {
throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.');
case 'teams':
if ($parts[2] === 'memberships') {
$permissionsChanged = $parts[4] ?? false;
$channels[] = 'memberships';
$channels[] = 'memberships.' . $parts[3];
$roles = ['team:' . $parts[1]];
} else {
$permissionsChanged = $parts[2] === 'create';
$channels[] = 'teams';
$channels[] = 'teams.' . $parts[1];
$roles = ['team:' . $parts[1]];
}
$channels[] = 'documents';
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents';
$channels[] = 'collections.' . $payload->getAttribute('$collection') . '.documents.' . $payload->getId();
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
break;
case strpos($event, 'storage.files') === 0:
if($bucket->isEmpty()) {
throw new \Exception('Bucket needs to be pased to Realtime for File events in the Storage.');
}
$channels[] = 'files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
$roles = $payload->getRead();
break;
case strpos($event, 'functions.executions.') === 0:
if (!empty($payload->getRead())) {
case 'collections':
if (in_array($parts[2], ['attributes', 'indexes'])) {
$channels[] = 'console';
$channels[] = 'executions';
$channels[] = 'executions.' . $payload->getId();
$channels[] = 'functions.' . $payload->getAttribute('functionId');
$roles = $payload->getRead();
$projectId = 'console';
$roles = ['team:' . $project->getAttribute('teamId')];
} elseif ($parts[2] === 'documents') {
if ($collection->isEmpty()) {
throw new \Exception('Collection needs to be passed to Realtime for Document events in the Database.');
}
$channels[] = 'documents';
$channels[] = 'collections.' . $payload->getCollection() . '.documents';
$channels[] = 'collections.' . $payload->getCollection() . '.documents.' . $payload->getId();
$roles = ($collection->getAttribute('permission') === 'collection') ? $collection->getRead() : $payload->getRead();
}
break;
case strpos($event, 'functions.deployments.') === 0:
$channels[] = 'console';
$roles = ['team:' . $project->getAttribute('teamId')];
case 'buckets':
if ($parts[2] === 'files') {
if($bucket->isEmpty()) {
throw new \Exception('Bucket needs to be pased to Realtime for File events in the Storage.');
}
$channels[] = 'files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files';
$channels[] = 'buckets.' . $payload->getAttribute('bucketId') . '.files.' . $payload->getId();
$roles = ($bucket->getAttribute('permission') === 'collection') ? $bucket->getRead() : $payload->getRead();
}
break;
case 'functions':
if ($parts[2] === 'executions') {
if (!empty($payload->getRead())) {
$channels[] = 'console';
$channels[] = 'executions';
$channels[] = 'executions.' . $payload->getId();
$channels[] = 'functions.' . $payload->getAttribute('functionId');
$roles = $payload->getRead();
}
} elseif ($parts[2] === 'deployments') {
$channels[] = 'console';
$roles = ['team:' . $project->getAttribute('teamId')];
}
break;
}

View file

@ -28,8 +28,8 @@ class FunctionsConsoleClientTest extends Scope
'funcKey3' => 'funcValue3',
],
'events' => [
'account.create',
'account.delete',
'users.*.create',
'users.*.delete',
],
'schedule' => '0 0 1 1 *',
'timeout' => 10,

View file

@ -33,8 +33,8 @@ class FunctionsCustomClientTest extends Scope
'funcKey3' => 'funcValue3',
],
'events' => [
'account.create',
'account.delete',
'users.*.create',
'users.*.delete',
],
'schedule' => '0 0 1 1 *',
'timeout' => 10,
@ -65,8 +65,8 @@ class FunctionsCustomClientTest extends Scope
'funcKey3' => 'funcValue3',
],
'events' => [
'account.create',
'account.delete',
'users.*.create',
'users.*.delete',
],
'schedule' => '0 0 1 1 *',
'timeout' => 10,

View file

@ -16,7 +16,7 @@ class FunctionsCustomServerTest extends Scope
use ProjectCustom;
use SideServer;
public function testCreate():array
public function testCreate(): array
{
/**
* Test for SUCCESS
@ -34,8 +34,8 @@ class FunctionsCustomServerTest extends Scope
'funcKey3' => 'funcValue3',
],
'events' => [
'account.create',
'account.delete',
'users.*.create',
'users.*.delete',
],
'schedule' => '0 0 1 1 *',
'timeout' => 10,
@ -56,12 +56,12 @@ class FunctionsCustomServerTest extends Scope
'funcKey3' => 'funcValue3',
], $response1['body']['vars']);
$this->assertEquals([
'account.create',
'account.delete',
'users.*.create',
'users.*.delete',
], $response1['body']['events']);
$this->assertEquals('0 0 1 1 *', $response1['body']['schedule']);
$this->assertEquals(10, $response1['body']['timeout']);
/**
* Test for FAILURE
*/
@ -74,7 +74,7 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testCreate
*/
public function testList(array $data):array
public function testList(array $data): array
{
/**
* Test for SUCCESS
@ -132,8 +132,8 @@ class FunctionsCustomServerTest extends Scope
'funcKey3' => 'funcValue3',
],
'events' => [
'account.create',
'account.delete',
'users.*.create',
'users.*.delete',
],
'schedule' => '0 0 1 1 *',
'timeout' => 10,
@ -193,7 +193,7 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testList
*/
public function testGet(array $data):array
public function testGet(array $data): array
{
/**
* Test for SUCCESS
@ -222,12 +222,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testGet
*/
public function testUpdate($data):array
public function testUpdate($data): array
{
/**
* Test for SUCCESS
*/
$response1 = $this->client->call(Client::METHOD_PUT, '/functions/'.$data['functionId'], array_merge([
$response1 = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -238,8 +238,8 @@ class FunctionsCustomServerTest extends Scope
'key6' => 'value6',
],
'events' => [
'account.update.name',
'account.update.email',
'users.*.update.name',
'users.*.update.email',
],
'schedule' => '0 0 1 1 *',
'timeout' => 5,
@ -257,12 +257,12 @@ class FunctionsCustomServerTest extends Scope
'key6' => 'value6',
], $response1['body']['vars']);
$this->assertEquals([
'account.update.name',
'account.update.email',
'users.*.update.name',
'users.*.update.email',
], $response1['body']['events']);
$this->assertEquals('0 0 1 1 *', $response1['body']['schedule']);
$this->assertEquals(5, $response1['body']['timeout']);
/**
* Test for FAILURE
*/
@ -273,16 +273,16 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testUpdate
*/
public function testCreateDeployment($data):array
public function testCreateDeployment($data): array
{
/**
* Test for SUCCESS
*/
$folder = 'php';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -306,16 +306,17 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testUpdate
*/
public function testCreateDeploymentLarge($data): array {
public function testCreateDeploymentLarge($data): array
{
/**
* Test for Large Code File SUCCESS
*/
$folder = 'php-large';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$chunkSize = 5*1024*1024;
$chunkSize = 5 * 1024 * 1024;
$handle = @fopen($code, "rb");
$mimeType = 'application/x-gzip';
$counter = 0;
@ -328,10 +329,10 @@ class FunctionsCustomServerTest extends Scope
while (!feof($handle)) {
$curlFile = new \CURLFile('data://' . $mimeType . ';base64,' . base64_encode(@fread($handle, $chunkSize)), $mimeType, 'php-large-fx.tar.gz');
$headers['content-range'] = 'bytes ' . ($counter * $chunkSize) . '-' . min(((($counter * $chunkSize) + $chunkSize) - 1), $size) . '/' . $size;
if(!empty($id)) {
if (!empty($id)) {
$headers['x-appwrite-id'] = $id;
}
$largeTag = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/deployments', array_merge($headers, $this->getHeaders()), [
$largeTag = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge($headers, $this->getHeaders()), [
'entrypoint' => 'index.php',
'code' => $curlFile,
]);
@ -345,19 +346,19 @@ class FunctionsCustomServerTest extends Scope
$this->assertIsInt($largeTag['body']['dateCreated']);
$this->assertEquals('index.php', $largeTag['body']['entrypoint']);
$this->assertGreaterThan(10000, $largeTag['body']['size']);
return $data;
}
/**
* @depends testCreateDeployment
*/
public function testUpdateDeployment($data):array
public function testUpdateDeployment($data): array
{
/**
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_PATCH, '/functions/'.$data['functionId'].'/deployments/'.$data['deploymentId'], array_merge([
$response = $this->client->call(Client::METHOD_PATCH, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
@ -367,7 +368,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertIsInt($response['body']['dateCreated']);
$this->assertIsInt($response['body']['dateUpdated']);
$this->assertEquals($data['deploymentId'], $response['body']['deployment']);
/**
* Test for FAILURE
*/
@ -378,12 +379,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testCreateDeployment
*/
public function testListDeployments(array $data):array
public function testListDeployments(array $data): array
{
/**
* Test for SUCCESS
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -396,7 +397,7 @@ class FunctionsCustomServerTest extends Scope
/**
* Test search queries
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders(), [
@ -409,7 +410,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(2, $function['body']['deployments']);
$this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']);
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders(), [
@ -422,7 +423,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(2, $function['body']['deployments']);
$this->assertEquals($function['body']['deployments'][0]['$id'], $data['deploymentId']);
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders(), [
@ -441,12 +442,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testCreateDeployment
*/
public function testGetDeployment(array $data):array
public function testGetDeployment(array $data): array
{
/**
* Test for SUCCESS
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments/' . $data['deploymentId'], array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -456,7 +457,7 @@ class FunctionsCustomServerTest extends Scope
/**
* Test for FAILURE
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments/x', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/x', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -469,12 +470,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testUpdateDeployment
*/
public function testCreateExecution($data):array
public function testCreateExecution($data): array
{
/**
* Test for SUCCESS
*/
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -496,7 +497,7 @@ class FunctionsCustomServerTest extends Scope
sleep(5);
$execution = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions/'.$executionId, array_merge([
$execution = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -529,12 +530,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testCreateExecution
*/
public function testListExecutions(array $data):array
public function testListExecutions(array $data): array
{
/**
* Test for SUCCESS
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -549,7 +550,7 @@ class FunctionsCustomServerTest extends Scope
* Test search queries
*/
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -562,7 +563,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertCount(1, $response['body']['executions']);
$this->assertEquals($data['functionId'], $response['body']['executions'][0]['functionId']);
$response = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions', array_merge([
$response = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -581,12 +582,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testUpdateDeployment
*/
public function testSyncCreateExecution($data):array
public function testSyncCreateExecution($data): array
{
/**
* Test for SUCCESS
*/
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$data['functionId'].'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -610,12 +611,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testListExecutions
*/
public function testGetExecution(array $data):array
public function testGetExecution(array $data): array
{
/**
* Test for SUCCESS
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions/' . $data['executionId'], array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/' . $data['executionId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -626,7 +627,7 @@ class FunctionsCustomServerTest extends Scope
/**
* Test for FAILURE
*/
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/executions/x', array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/executions/x', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -639,12 +640,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testGetExecution
*/
public function testDeleteDeployment($data):array
public function testDeleteDeployment($data): array
{
/**
* Test for SUCCESS
*/
$function = $this->client->call(Client::METHOD_DELETE, '/functions/'.$data['functionId'].'/deployments/' . $data['deploymentId'], array_merge([
$function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -652,11 +653,11 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(204, $function['headers']['status-code']);
$this->assertEmpty($function['body']);
$function = $this->client->call(Client::METHOD_GET, '/functions/'.$data['functionId'].'/deployments/' . $data['deploymentId'], array_merge([
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/deployments/' . $data['deploymentId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $function['headers']['status-code']);
/**
@ -669,12 +670,12 @@ class FunctionsCustomServerTest extends Scope
/**
* @depends testCreateDeployment
*/
public function testDelete($data):array
public function testDelete($data): array
{
/**
* Test for SUCCESS
*/
$function = $this->client->call(Client::METHOD_DELETE, '/functions/'. $data['functionId'], array_merge([
$function = $this->client->call(Client::METHOD_DELETE, '/functions/' . $data['functionId'], array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -686,7 +687,7 @@ class FunctionsCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(404, $function['headers']['status-code']);
/**
@ -702,15 +703,15 @@ class FunctionsCustomServerTest extends Scope
$entrypoint = 'index.php';
$timeout = 2;
$folder = 'timeout';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => 'unique()',
'name' => 'Test '.$name,
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [],
'events' => [],
@ -722,7 +723,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -735,8 +736,8 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(20);
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -744,12 +745,12 @@ class FunctionsCustomServerTest extends Scope
]);
$executionId = $execution['body']['$id'] ?? '';
$this->assertEquals(201, $execution['headers']['status-code']);
sleep(5);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -768,7 +769,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals($executions['body']['executions'][0]['stderr'], 'Execution timed out.');
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
@ -786,7 +787,7 @@ class FunctionsCustomServerTest extends Scope
$entrypoint = 'index.php';
$timeout = 2;
$folder = 'php-fn';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$function = $this->client->call(Client::METHOD_POST, '/functions', array_merge([
@ -794,7 +795,7 @@ class FunctionsCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => 'unique()',
'name' => 'Test '.$name,
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [],
'events' => [],
@ -806,7 +807,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -820,14 +821,14 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(10);
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/'.$functionId.'/deployments/'.$deploymentId, array_merge([
$deployment = $this->client->call(Client::METHOD_PATCH, '/functions/' . $functionId . '/deployments/' . $deploymentId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), []);
$this->assertEquals(200, $deployment['headers']['status-code']);
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -835,14 +836,14 @@ class FunctionsCustomServerTest extends Scope
]);
$executionId = $execution['body']['$id'] ?? '';
$this->assertEquals(201, $execution['headers']['status-code']);
$executionId = $execution['body']['$id'] ?? '';
sleep(10);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -852,7 +853,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertEquals('completed', $executions['body']['status']);
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
$this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals('Test ' . $name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
$this->assertEquals('PHP', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
@ -864,11 +865,11 @@ class FunctionsCustomServerTest extends Scope
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($executions['headers']['status-code'], 200);
$this->assertEquals($executions['body']['total'], 1);
$this->assertIsArray($executions['body']['executions']);
@ -878,7 +879,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
@ -892,9 +893,9 @@ class FunctionsCustomServerTest extends Scope
{
$name = 'node-17.0';
$folder = 'node';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$entrypoint = 'index.js';
$timeout = 2;
@ -903,7 +904,7 @@ class FunctionsCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => 'unique()',
'name' => 'Test '.$name,
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
@ -917,7 +918,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -932,7 +933,7 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(10);
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -940,14 +941,14 @@ class FunctionsCustomServerTest extends Scope
]);
$executionId = $execution['body']['$id'] ?? '';
$this->assertEquals(201, $execution['headers']['status-code']);
$executionId = $execution['body']['$id'] ?? '';
sleep(10);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -957,7 +958,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertEquals('completed', $executions['body']['status']);
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
$this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals('Test ' . $name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
$this->assertEquals('Node.js', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
@ -970,11 +971,11 @@ class FunctionsCustomServerTest extends Scope
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($executions['headers']['status-code'], 200);
$this->assertEquals($executions['body']['total'], 1);
$this->assertIsArray($executions['body']['executions']);
@ -984,7 +985,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
@ -997,7 +998,7 @@ class FunctionsCustomServerTest extends Scope
{
$name = 'python-3.9';
$folder = 'python';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$entrypoint = 'main.py';
@ -1008,7 +1009,7 @@ class FunctionsCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => 'unique()',
'name' => 'Test '.$name,
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
@ -1022,7 +1023,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -1037,7 +1038,7 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(30);
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -1045,14 +1046,14 @@ class FunctionsCustomServerTest extends Scope
]);
$executionId = $execution['body']['$id'] ?? '';
$this->assertEquals(201, $execution['headers']['status-code']);
$executionId = $execution['body']['$id'] ?? '';
sleep(30);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -1062,7 +1063,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertEquals('completed', $executions['body']['status']);
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
$this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals('Test ' . $name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
$this->assertEquals('Python', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
@ -1075,11 +1076,11 @@ class FunctionsCustomServerTest extends Scope
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($executions['headers']['status-code'], 200);
$this->assertEquals($executions['body']['total'], 1);
$this->assertIsArray($executions['body']['executions']);
@ -1089,7 +1090,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
@ -1102,9 +1103,9 @@ class FunctionsCustomServerTest extends Scope
{
$name = 'dart-2.15';
$folder = 'dart';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$entrypoint = 'main.dart';
$timeout = 2;
@ -1113,7 +1114,7 @@ class FunctionsCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => 'unique()',
'name' => 'Test '.$name,
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
@ -1127,7 +1128,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -1142,7 +1143,7 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(40);
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -1150,14 +1151,14 @@ class FunctionsCustomServerTest extends Scope
]);
$executionId = $execution['body']['$id'] ?? '';
$this->assertEquals(201, $execution['headers']['status-code']);
$executionId = $execution['body']['$id'] ?? '';
sleep(10);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -1167,7 +1168,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertEquals('completed', $executions['body']['status']);
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
$this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals('Test ' . $name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
$this->assertEquals('Dart', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
@ -1180,11 +1181,11 @@ class FunctionsCustomServerTest extends Scope
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($executions['headers']['status-code'], 200);
$this->assertEquals($executions['body']['total'], 1);
$this->assertIsArray($executions['body']['executions']);
@ -1194,7 +1195,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
@ -1207,9 +1208,9 @@ class FunctionsCustomServerTest extends Scope
{
$name = 'ruby-3.1';
$folder = 'ruby';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
$code = realpath(__DIR__ . '/../../../resources/functions') . "/$folder/code.tar.gz";
$this->packageCode($folder);
$entrypoint = 'main.rb';
$timeout = 2;
@ -1218,7 +1219,7 @@ class FunctionsCustomServerTest extends Scope
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'functionId' => 'unique()',
'name' => 'Test '.$name,
'name' => 'Test ' . $name,
'runtime' => $name,
'vars' => [
'CUSTOM_VARIABLE' => 'variable',
@ -1232,7 +1233,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(201, $function['headers']['status-code']);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -1247,7 +1248,7 @@ class FunctionsCustomServerTest extends Scope
// Allow build step to run
sleep(30);
$execution = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/executions', array_merge([
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -1255,14 +1256,14 @@ class FunctionsCustomServerTest extends Scope
]);
$executionId = $execution['body']['$id'] ?? '';
$this->assertEquals(201, $execution['headers']['status-code']);
$executionId = $execution['body']['$id'] ?? '';
sleep(10);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions/' . $executionId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -1272,7 +1273,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertEquals(200, $executions['headers']['status-code']);
$this->assertEquals('completed', $executions['body']['status']);
$this->assertEquals($functionId, $output['APPWRITE_FUNCTION_ID']);
$this->assertEquals('Test '.$name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals('Test ' . $name, $output['APPWRITE_FUNCTION_NAME']);
$this->assertEquals($deploymentId, $output['APPWRITE_FUNCTION_DEPLOYMENT']);
$this->assertEquals('http', $output['APPWRITE_FUNCTION_TRIGGER']);
$this->assertEquals('Ruby', $output['APPWRITE_FUNCTION_RUNTIME_NAME']);
@ -1285,11 +1286,11 @@ class FunctionsCustomServerTest extends Scope
$this->assertEmpty($output['APPWRITE_FUNCTION_JWT']);
$this->assertEquals($this->getProject()['$id'], $output['APPWRITE_FUNCTION_PROJECT_ID']);
$executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions', array_merge([
$executions = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/executions', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals($executions['headers']['status-code'], 200);
$this->assertEquals($executions['body']['total'], 1);
$this->assertIsArray($executions['body']['executions']);
@ -1299,7 +1300,7 @@ class FunctionsCustomServerTest extends Scope
$this->assertStringContainsString('foobar', $executions['body']['executions'][0]['stdout']);
// Cleanup : Delete function
$response = $this->client->call(Client::METHOD_DELETE, '/functions/'. $functionId, [
$response = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
@ -1314,7 +1315,7 @@ class FunctionsCustomServerTest extends Scope
// $folder = 'swift';
// $code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
// $this->packageCode($folder);
// $entrypoint = 'index.swift';
// $timeout = 5;
@ -1360,11 +1361,11 @@ class FunctionsCustomServerTest extends Scope
// ]);
// $executionId = $execution['body']['$id'] ?? '';
// $this->assertEquals(201, $execution['headers']['status-code']);
// $executionId = $execution['body']['$id'] ?? '';
// sleep(10);
// $executions = $this->client->call(Client::METHOD_GET, '/functions/'.$functionId.'/executions/'.$executionId, array_merge([
@ -1394,7 +1395,7 @@ class FunctionsCustomServerTest extends Scope
// 'content-type' => 'application/json',
// 'x-appwrite-project' => $this->getProject()['$id'],
// ], $this->getHeaders()));
// $this->assertEquals($executions['headers']['status-code'], 200);
// $this->assertEquals($executions['body']['total'], 1);
// $this->assertIsArray($executions['body']['executions']);
@ -1432,6 +1433,5 @@ class FunctionsCustomServerTest extends Scope
$this->assertArrayHasKey('image', $runtime);
$this->assertArrayHasKey('base', $runtime);
$this->assertArrayHasKey('supports', $runtime);
}
}
}

View file

@ -278,7 +278,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.update.name', $response['data']['event']);
$this->assertEquals("users.{$userId}.update.name", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($name, $response['data']['payload']['name']);
@ -307,7 +307,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.update.password', $response['data']['event']);
$this->assertEquals("users.{$userId}.update.password", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($name, $response['data']['payload']['name']);
@ -335,7 +335,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.update.email', $response['data']['event']);
$this->assertEquals("users.{$userId}.update.email", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals('torsten@appwrite.io', $response['data']['payload']['email']);
@ -343,7 +343,7 @@ class RealtimeCustomClientTest extends Scope
/**
* Test Account Verification Create
*/
$this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
$verification = $this->client->call(Client::METHOD_POST, '/account/verification', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
@ -362,7 +362,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.verification.create', $response['data']['event']);
$this->assertEquals("users.{$userId}.verification.{$verification['body']['$id']}.create", $response['data']['event']);
$lastEmail = $this->getLastEmail();
$verification = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256);
@ -370,7 +370,7 @@ class RealtimeCustomClientTest extends Scope
/**
* Test Account Verification Complete
*/
$response = $this->client->call(Client::METHOD_PUT, '/account/verification', array_merge([
$verification = $this->client->call(Client::METHOD_PUT, '/account/verification', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
@ -390,7 +390,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.verification.update', $response['data']['event']);
$this->assertEquals("users.{$userId}.verification.{$verification['body']['$id']}.update", $response['data']['event']);
/**
* Test Acoount Prefs Update
@ -417,7 +417,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.update.prefs', $response['data']['event']);
$this->assertEquals("users.{$userId}.update.prefs", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
/**
@ -445,7 +445,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.sessions.create', $response['data']['event']);
$this->assertEquals("users.{$userId}.sessions.{$sessionNewId}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
/**
@ -468,13 +468,13 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.sessions.delete', $response['data']['event']);
$this->assertEquals("users.{$userId}.sessions.{$sessionNewId}.delete", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
/**
* Test Account Create Recovery
*/
$this->client->call(Client::METHOD_POST, '/account/recovery', array_merge([
$recovery = $this->client->call(Client::METHOD_POST, '/account/recovery', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
@ -482,7 +482,7 @@ class RealtimeCustomClientTest extends Scope
'email' => 'torsten@appwrite.io',
'url' => 'http://localhost/recovery',
]);
$recoveryId = $recovery['body']['$id'];
$response = json_decode($client->receive(), true);
$lastEmail = $this->getLastEmail();
@ -496,7 +496,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.recovery.create', $response['data']['event']);
$this->assertEquals("users.{$userId}.recovery.{$recoveryId}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$response = $this->client->call(Client::METHOD_PUT, '/account/recovery', array_merge([
@ -520,7 +520,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertContains('account', $response['data']['channels']);
$this->assertContains('account.' . $userId, $response['data']['channels']);
$this->assertEquals('account.recovery.update', $response['data']['event']);
$this->assertEquals("users.{$userId}.recovery.{$recoveryId}.update", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$client->close();
@ -610,7 +610,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.create', $response['data']['event']);
$this->assertEquals("collections.{$data['actorsId']}.documents.{$document['body']['$id']}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
@ -642,7 +642,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $data['documentId'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.update', $response['data']['event']);
$this->assertEquals("collections.{$data['actorsId']}.documents.{$data['documentId']}.update", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
@ -680,7 +680,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.delete', $response['data']['event']);
$this->assertEquals("collections.{$data['actorsId']}.documents.{$document['body']['$id']}.delete", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
@ -771,7 +771,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $actors['body']['$id'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.create', $response['data']['event']);
$this->assertEquals("collections.{$data['actorsId']}.documents.{$document['body']['$id']}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans');
@ -802,7 +802,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $data['documentId'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.update', $response['data']['event']);
$this->assertEquals("collections.{$data['actorsId']}.documents.{$document['body']['$id']}.update", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Chris Evans 2');
@ -840,7 +840,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('documents', $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents.' . $document['body']['$id'], $response['data']['channels']);
$this->assertContains('collections.' . $data['actorsId'] . '.documents', $response['data']['channels']);
$this->assertEquals('database.documents.delete', $response['data']['event']);
$this->assertEquals("collections.{$data['actorsId']}.documents.{$document['body']['$id']}.delete", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals($response['data']['payload']['name'], 'Bradley Cooper');
@ -908,7 +908,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('files', $response['data']['channels']);
$this->assertContains('buckets.' . $data['bucketId'] . '.files.' . $file['body']['$id'], $response['data']['channels']);
$this->assertContains('buckets.' . $data['bucketId'] . '.files', $response['data']['channels']);
$this->assertEquals('storage.files.create', $response['data']['event']);
$this->assertEquals("buckets.{$data['bucketId']}.files.{$file['body']['$id']}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$data['fileId'] = $file['body']['$id'];
@ -935,7 +935,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('files', $response['data']['channels']);
$this->assertContains('buckets.' . $data['bucketId'] . '.files.' . $file['body']['$id'], $response['data']['channels']);
$this->assertContains('buckets.' . $data['bucketId'] . '.files', $response['data']['channels']);
$this->assertEquals('storage.files.update', $response['data']['event']);
$this->assertEquals("buckets.{$data['bucketId']}.files.{$file['body']['$id']}.update", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
/**
@ -957,7 +957,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('files', $response['data']['channels']);
$this->assertContains('buckets.' . $data['bucketId'] . '.files.' . $file['body']['$id'], $response['data']['channels']);
$this->assertContains('buckets.' . $data['bucketId'] . '.files', $response['data']['channels']);
$this->assertEquals('storage.files.delete', $response['data']['event']);
$this->assertEquals("buckets.{$data['bucketId']}.files.{$file['body']['$id']}.delete", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$client->close();
@ -1010,7 +1010,7 @@ class RealtimeCustomClientTest extends Scope
$stdout= '';
$code = realpath(__DIR__ . '/../../../resources/functions'). "/$folder/code.tar.gz";
Console::execute('cd '.realpath(__DIR__ . "/../../../resources/functions") . "/$folder && tar --exclude code.tar.gz -czf code.tar.gz .", '', $stdout, $stderr);
$deployment = $this->client->call(Client::METHOD_POST, '/functions/'.$functionId.'/deployments', array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id'],
@ -1058,7 +1058,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('executions', $response['data']['channels']);
$this->assertContains('executions.' . $execution['body']['$id'], $response['data']['channels']);
$this->assertContains('functions.' . $execution['body']['functionId'], $response['data']['channels']);
$this->assertEquals('functions.executions.create', $response['data']['event']);
$this->assertEquals("functions.{$functionId}.executions.{$execution['body']['$id']}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$this->assertArrayHasKey('type', $responseUpdate);
@ -1071,7 +1071,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertContains('executions', $responseUpdate['data']['channels']);
$this->assertContains('executions.' . $execution['body']['$id'], $responseUpdate['data']['channels']);
$this->assertContains('functions.' . $execution['body']['functionId'], $responseUpdate['data']['channels']);
$this->assertEquals('functions.executions.update', $responseUpdate['data']['event']);
$this->assertEquals("functions.{$functionId}.executions.{$execution['body']['$id']}.update", $responseUpdate['data']['event']);
$this->assertNotEmpty($responseUpdate['data']['payload']);
$client->close();
@ -1134,7 +1134,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('teams', $response['data']['channels']);
$this->assertContains('teams.' . $teamId, $response['data']['channels']);
$this->assertEquals('teams.create', $response['data']['event']);
$this->assertEquals("teams.{$teamId}.create", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
/**
@ -1160,7 +1160,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('teams', $response['data']['channels']);
$this->assertContains('teams.' . $teamId, $response['data']['channels']);
$this->assertEquals('teams.update', $response['data']['event']);
$this->assertEquals("teams.{$teamId}.update", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$client->close();
@ -1224,7 +1224,7 @@ class RealtimeCustomClientTest extends Scope
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('memberships', $response['data']['channels']);
$this->assertContains('memberships.' . $membershipId, $response['data']['channels']);
$this->assertEquals('teams.memberships.update', $response['data']['event']);
$this->assertEquals("teams.{$teamId}.memberships.{$membershipId}.update", $response['data']['event']);
$this->assertNotEmpty($response['data']['payload']);
$client->close();