1
0
Fork 0
mirror of synced 2024-06-29 19:50:26 +12:00

Merge remote-tracking branch 'origin/1.5.x' into feat-rc-sdks

# Conflicts:
#	app/config/specs/open-api3-latest-client.json
#	app/config/specs/open-api3-latest-console.json
#	app/config/specs/open-api3-latest-server.json
#	app/config/specs/swagger2-latest-client.json
#	app/config/specs/swagger2-latest-console.json
#	app/config/specs/swagger2-latest-server.json
#	composer.lock
This commit is contained in:
Jake Barnby 2024-02-23 18:01:59 +13:00
commit 7c42a59c85
No known key found for this signature in database
GPG key ID: C437A8CC85B96E9C
29 changed files with 476 additions and 372 deletions

View file

@ -287,6 +287,11 @@ return [
'description' => 'A target with the same ID already exists.',
'code' => 409,
],
Exception::USER_API_KEY_AND_SESSION_SET => [
'name' => Exception::USER_API_KEY_AND_SESSION_SET,
'description' => 'API key and session used in the same request. Use either `setSession` or `setKey`. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering',
'code' => 403,
],
/** Teams */
Exception::TEAM_NOT_FOUND => [

View file

@ -9,8 +9,7 @@ $member = [
'console',
'graphql',
'sessions.write',
'accounts.read',
'accounts.write',
'account',
'teams.read',
'teams.write',
'documents.read',

View file

@ -1,12 +1,6 @@
<?php
return [ // List of publicly visible scopes
'accounts.read' => [
'description' => 'Access to read your active user account',
],
'accounts.write' => [
'description' => 'Access to create, update, and delete your active user account',
],
'sessions.write' => [
'description' => 'Access to create, update, and delete user sessions',
],

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -342,14 +342,11 @@ App::get('/v1/account/sessions/oauth2/:provider')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn($node) => (!$node['mock'])))) . '.')
->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('token', false, new Boolean(true), 'Include token credentials in the final redirect, useful for server-side integrations, or when cookies are not available.', true)
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->inject('request')
->inject('response')
->inject('project')
->action(function (string $provider, string $success, string $failure, mixed $token, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$token = in_array($token, ['true', true], true);
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$protocol = $request->getProtocol();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
@ -388,7 +385,77 @@ App::get('/v1/account/sessions/oauth2/:provider')
$oauth2 = new $className($appId, $appSecret, $callback, [
'success' => $success,
'failure' => $failure,
'token' => $token,
'token' => false,
], $scopes);
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($oauth2->getLoginURL());
});
App::get('/v1/account/tokens/oauth2/:provider')
->desc('Create OAuth2 token')
->groups(['api', 'account'])
->label('error', __DIR__ . '/../../views/general/error.phtml')
->label('scope', 'sessions.write')
->label('sdk.auth', [])
->label('sdk.hideServer', true)
->label('sdk.namespace', 'account')
->label('sdk.method', 'createOAuth2Token')
->label('sdk.description', '/docs/references/account/create-token-oauth2.md')
->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY)
->label('sdk.response.type', Response::CONTENT_TYPE_HTML)
->label('sdk.methodType', 'webAuth')
->label('abuse-limit', 50)
->label('abuse-key', 'ip:{ip}')
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('oAuthProviders'), fn($node) => (!$node['mock'])))) . '.')
->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project\'s platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
->param('scopes', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long.', true)
->inject('request')
->inject('response')
->inject('project')
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$protocol = $request->getProtocol();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
if (!$providerEnabled) {
throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please enable the provider from your ' . APP_NAME . ' console to continue.');
}
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
if (!empty($appSecret) && isset($appSecret['version'])) {
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
$appSecret = OpenSSL::decrypt($appSecret['data'], $appSecret['method'], $key, 0, \hex2bin($appSecret['iv']), \hex2bin($appSecret['tag']));
}
if (empty($appId) || empty($appSecret)) {
throw new Exception(Exception::PROJECT_PROVIDER_DISABLED, 'This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.');
}
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
if (!\class_exists($className)) {
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
if (empty($success)) {
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
}
if (empty($failure)) {
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
}
$oauth2 = new $className($appId, $appSecret, $callback, [
'success' => $success,
'failure' => $failure,
'token' => true,
], $scopes);
$response
@ -898,7 +965,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
App::get('/v1/account/identities')
->desc('List Identities')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listIdentities')
@ -954,7 +1021,7 @@ App::get('/v1/account/identities')
App::delete('/v1/account/identities/:identityId')
->desc('Delete identity')
->groups(['api', 'account'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].identities.[identityId].delete')
->label('audits.event', 'identity.delete')
->label('audits.resource', 'identity/{request.$identityId}')
@ -1940,7 +2007,7 @@ App::post('/v1/account/sessions/anonymous')
App::post('/v1/account/jwt')
->desc('Create JWT')
->groups(['api', 'account', 'auth'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('auth.type', 'jwt')
->label('sdk.auth', [])
->label('sdk.namespace', 'account')
@ -1987,7 +2054,7 @@ App::post('/v1/account/jwt')
App::get('/v1/account')
->desc('Get account')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'get')
@ -2010,7 +2077,7 @@ App::get('/v1/account')
App::get('/v1/account/prefs')
->desc('Get account preferences')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'getPrefs')
@ -2032,7 +2099,7 @@ App::get('/v1/account/prefs')
App::get('/v1/account/sessions')
->desc('List sessions')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listSessions')
@ -2067,7 +2134,7 @@ App::get('/v1/account/sessions')
App::get('/v1/account/logs')
->desc('List logs')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listLogs')
@ -2132,7 +2199,7 @@ App::get('/v1/account/logs')
App::get('/v1/account/sessions/:sessionId')
->desc('Get session')
->groups(['api', 'account'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'getSession')
@ -2174,7 +2241,7 @@ App::patch('/v1/account/name')
->desc('Update name')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.name')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@ -2207,7 +2274,7 @@ App::patch('/v1/account/password')
->desc('Update password')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.password')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
@ -2276,7 +2343,7 @@ App::patch('/v1/account/email')
->desc('Update email')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.email')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@ -2368,7 +2435,7 @@ App::patch('/v1/account/phone')
->desc('Update phone')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.phone')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@ -2449,7 +2516,7 @@ App::patch('/v1/account/prefs')
->desc('Update preferences')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.prefs')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@ -2482,7 +2549,7 @@ App::patch('/v1/account/status')
->desc('Update status')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.status')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
@ -2524,7 +2591,7 @@ App::patch('/v1/account/status')
App::delete('/v1/account/sessions/:sessionId')
->desc('Delete session')
->groups(['api', 'account'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].delete')
->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{user.$id}')
@ -2604,7 +2671,7 @@ App::delete('/v1/account/sessions/:sessionId')
App::patch('/v1/account/sessions/:sessionId')
->desc('Update (or renew) a session')
->groups(['api', 'account'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].update')
->label('audits.event', 'session.update')
->label('audits.resource', 'user/{response.userId}')
@ -2680,7 +2747,7 @@ App::patch('/v1/account/sessions/:sessionId')
App::delete('/v1/account/sessions')
->desc('Delete sessions')
->groups(['api', 'account'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[sessionId].delete')
->label('audits.event', 'session.delete')
->label('audits.resource', 'user/{user.$id}')
@ -3007,7 +3074,7 @@ App::put('/v1/account/recovery')
App::post('/v1/account/verification')
->desc('Create email verification')
->groups(['api', 'account'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].verification.[tokenId].create')
->label('audits.event', 'verification.create')
->label('audits.resource', 'user/{response.userId}')
@ -3227,7 +3294,7 @@ App::put('/v1/account/verification')
App::post('/v1/account/verification/phone')
->desc('Create phone verification')
->groups(['api', 'account', 'auth'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('auth.type', 'phone')
->label('event', 'users.[userId].verification.[tokenId].create')
->label('audits.event', 'verification.create')
@ -3398,7 +3465,7 @@ App::patch('/v1/account/mfa')
->desc('Update MFA')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
@ -3431,7 +3498,7 @@ App::patch('/v1/account/mfa')
App::get('/v1/account/mfa/factors')
->desc('List Factors')
->groups(['api', 'account', 'mfa'])
->label('scope', 'accounts.read')
->label('scope', 'account')
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'account')
->label('sdk.method', 'listFactors')
@ -3458,7 +3525,7 @@ App::post('/v1/account/mfa/:type')
->desc('Add Authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
@ -3517,7 +3584,7 @@ App::put('/v1/account/mfa/:type')
->desc('Verify Authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].update.mfa')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
@ -3573,7 +3640,7 @@ App::delete('/v1/account/mfa/:type')
->desc('Delete Authenticator')
->groups(['api', 'account'])
->label('event', 'users.[userId].delete.mfa')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.update')
->label('audits.resource', 'user/{response.$id}')
->label('audits.userId', '{response.$id}')
@ -3622,7 +3689,7 @@ App::delete('/v1/account/mfa/:type')
App::post('/v1/account/mfa/challenge')
->desc('Create 2FA Challenge')
->groups(['api', 'account', 'mfa'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].challenges.[challengeId].create')
->label('audits.event', 'challenge.create')
->label('audits.resource', 'user/{response.userId}')
@ -3715,7 +3782,7 @@ App::post('/v1/account/mfa/challenge')
App::put('/v1/account/mfa/challenge')
->desc('Create MFA Challenge (confirmation)')
->groups(['api', 'account', 'mfa'])
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('event', 'users.[userId].sessions.[tokenId].create')
->label('audits.event', 'challenges.update')
->label('audits.resource', 'user/{response.userId}')
@ -3781,7 +3848,7 @@ App::delete('/v1/account')
->desc('Delete account')
->groups(['api', 'account'])
->label('event', 'users.[userId].delete')
->label('scope', 'accounts.write')
->label('scope', 'account')
->label('audits.event', 'user.delete')
->label('audits.resource', 'user/{response.$id}')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])

View file

@ -195,14 +195,14 @@ App::init()
$authKey = $request->getHeader('x-appwrite-key', '');
if (!empty($authKey)) { // API Key authentication
// Do not allow API key and session to be set at the same time
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
// Check if given key match project API keys
$key = $project->find('secret', $authKey, 'keys');
/*
* Try app auth when we have project key and no user
* Mock user to app and grant API key scopes in addition to default app scopes
*/
if ($key && $user->isEmpty()) {
if ($key) {
$user = new Document([
'$id' => '',
'status' => true,

View file

@ -3,6 +3,7 @@
require_once __DIR__ . '/../vendor/autoload.php';
use Appwrite\Utopia\Response;
use Swoole\Constant;
use Swoole\Process;
use Swoole\Http\Server;
use Swoole\Http\Request as SwooleRequest;
@ -20,12 +21,15 @@ use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Swoole\Files;
use Appwrite\Utopia\Request;
use Swoole\Coroutine;
use Utopia\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Pools\Group;
$http = new Server("0.0.0.0", App::getEnv('PORT', 80));
$http = new Server(
host: "0.0.0.0",
port: App::getEnv('PORT', 80),
mode: SWOOLE_PROCESS,
);
$payloadSize = 6 * (1024 * 1024); // 6MB
$workerNumber = swoole_cpu_num() * intval(App::getEnv('_APP_WORKER_PER_CORE', 6));
@ -34,23 +38,21 @@ $http
->set([
'worker_num' => $workerNumber,
'open_http2_protocol' => true,
// 'document_root' => __DIR__.'/../public',
// 'enable_static_handler' => true,
'http_compression' => true,
'http_compression_level' => 6,
'package_max_length' => $payloadSize,
'buffer_output_size' => $payloadSize,
]);
$http->on('WorkerStart', function ($server, $workerId) {
$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) {
Console::success('Worker ' . ++$workerId . ' started successfully');
});
$http->on('BeforeReload', function ($server, $workerId) {
$http->on(Constant::EVENT_BEFORE_RELOAD, function ($server, $workerId) {
Console::success('Starting reload...');
});
$http->on('AfterReload', function ($server, $workerId) {
$http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
@ -58,7 +60,7 @@ Files::load(__DIR__ . '/../console');
include __DIR__ . '/controllers/general.php';
$http->on('start', function (Server $http) use ($payloadSize, $register) {
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
$app = new App('UTC');
go(function () use ($register, $app) {

View file

@ -50,7 +50,7 @@
"utopia-php/cache": "0.9.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.48.*",
"utopia-php/database": "0.48.2",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",

28
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "1cf29cdafc7eb9a8b46227a8ecb31708",
"content-hash": "36c4412b5422eaf921f20524107be5bb",
"packages": [
{
"name": "adhocore/jwt",
@ -1552,16 +1552,16 @@
},
{
"name": "utopia-php/database",
"version": "0.48.3",
"version": "0.48.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "c7dd97d92f52a0aec9951e0b02309a100f3f24a9"
"reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/c7dd97d92f52a0aec9951e0b02309a100f3f24a9",
"reference": "c7dd97d92f52a0aec9951e0b02309a100f3f24a9",
"url": "https://api.github.com/repos/utopia-php/database/zipball/0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4",
"reference": "0a231a2874fdbc0cf2ae2170b3f132fdee0ddfd4",
"shasum": ""
},
"require": {
@ -1569,7 +1569,7 @@
"ext-pdo": "*",
"php": ">=8.0",
"utopia-php/cache": "0.9.*",
"utopia-php/framework": "0.33.*",
"utopia-php/framework": "0.*.*",
"utopia-php/mongo": "0.3.*"
},
"require-dev": {
@ -1602,9 +1602,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.48.3"
"source": "https://github.com/utopia-php/database/tree/0.48.2"
},
"time": "2024-02-21T08:32:09+00:00"
"time": "2024-02-02T14:10:14+00:00"
},
{
"name": "utopia-php/domains",
@ -3131,16 +3131,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.0.0",
"version": "v5.0.1",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc"
"reference": "2218c2252c874a4624ab2f613d86ac32d227bc69"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"reference": "4a21235f7e56e713259a6f76bf4b5ea08502b9dc",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/2218c2252c874a4624ab2f613d86ac32d227bc69",
"reference": "2218c2252c874a4624ab2f613d86ac32d227bc69",
"shasum": ""
},
"require": {
@ -3183,9 +3183,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.0.1"
},
"time": "2024-01-07T17:17:35+00:00"
"time": "2024-02-21T19:24:10+00:00"
},
{
"name": "phar-io/manifest",

View file

@ -0,0 +1,5 @@
Allow the user to login to their account using the OAuth2 provider of their choice. Each OAuth2 provider should be enabled from the Appwrite console first. Use the success and failure arguments to provide a redirect URL's back to your app when login is completed.
If authentication succeeds, `userId` and `secret` of a token will be appended to the success URL as query parameters. These can be used to create a new session using the [Create session](https://appwrite.io/docs/references/cloud/client-web/account#createSession) endpoint.
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](https://appwrite.io/docs/authentication-security#limits).

View file

@ -97,6 +97,7 @@ class Exception extends \Exception
public const USER_DELETION_PROHIBITED = 'user_deletion_prohibited';
public const USER_TARGET_NOT_FOUND = 'user_target_not_found';
public const USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists';
public const USER_API_KEY_AND_SESSION_SET = 'user_key_and_session_set';
/** Teams */
public const TEAM_NOT_FOUND = 'team_not_found';

View file

@ -120,12 +120,6 @@ class Specs extends Action
'description' => 'The user session to authenticate with',
'in' => 'header',
],
'ForwardedFor' => [
'type' => 'apiKey',
'name' => 'X-Forwarded-For',
'description' => 'The IP address of the client that made the request',
'in' => 'header',
],
'ForwardedUserAgent' => [
'type' => 'apiKey',
'name' => 'X-Forwarded-User-Agent',

View file

@ -32,6 +32,16 @@ class UsageTest extends Scope
protected static string $formatTz = 'Y-m-d\TH:i:s.vP';
protected function getConsoleHeaders(): array
{
return [
'origin' => 'http://localhost',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-mode' => 'admin',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
];
}
protected function validateDates(array $metrics): void
{
foreach ($metrics as $metric) {
@ -54,48 +64,56 @@ class UsageTest extends Scope
public function testPrepareUsersStats(): array
{
$project = $this->getProject(true);
$projectId = $project['$id'];
$headers['x-appwrite-project'] = $project['$id'];
$headers['x-appwrite-key'] = $project['apiKey'];
$headers['content-type'] = 'application/json';
$usersTotal = 0;
$usersTotal = 0;
$requestsTotal = 0;
for ($i = 0; $i < self::CREATE; $i++) {
$email = uniqid() . 'user@usage.test';
$password = 'password';
$name = uniqid() . 'User';
$res = $this->client->call(
$params = [
'userId' => 'unique()',
'email' => uniqid() . 'user@usage.test',
'password' => 'password',
'name' => uniqid() . 'User',
];
$response = $this->client->call(
Client::METHOD_POST,
'/users',
$headers,
[
'userId' => 'unique()',
'email' => $email,
'password' => $password,
'name' => $name,
]
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
$params
);
$this->assertEquals($email, $res['body']['email']);
$this->assertNotEmpty($res['body']['$id']);
$usersTotal++;
$requestsTotal++;
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals($params['email'], $response['body']['email']);
$this->assertNotEmpty($response['body']['$id']);
$usersTotal += 1;
$requestsTotal += 1;
if ($i < (self::CREATE / 2)) {
$userId = $res['body']['$id'];
$res = $this->client->call(Client::METHOD_DELETE, '/users/' . $userId, $headers);
$this->assertEmpty($res['body']);
$requestsTotal++;
$usersTotal--;
$userId = $response['body']['$id'];
$response = $this->client->call(
Client::METHOD_DELETE,
'/users/' . $userId,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders())
);
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEmpty($response['body']);
$requestsTotal += 1;
$usersTotal -= 1;
}
}
return [
'projectId' => $projectId,
'headers' => $headers,
'usersTotal' => $usersTotal,
'usersTotal' => $usersTotal,
'requestsTotal' => $requestsTotal
];
}
@ -108,74 +126,62 @@ class UsageTest extends Scope
{
sleep(self::WAIT);
$projectId = $data['projectId'];
$headers = $data['headers'];
$usersTotal = $data['usersTotal'];
$requestsTotal = $data['requestsTotal'];
$consoleHeaders = [
'origin' => 'http://localhost',
'x-appwrite-project' => 'console',
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
'x-appwrite-project' => $projectId,
'x-appwrite-mode' => 'admin',
];
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/project/usage',
$consoleHeaders,
$this->getConsoleHeaders(),
[
'period' => '1h',
'startDate' => self::getToday(),
'endDate' => self::getTomorrow(),
]
);
$res = $res['body'];
$this->assertEquals(12, count($res));
$this->validateDates($res['network']);
$this->validateDates($res['requests']);
$this->validateDates($res['users']);
$this->assertArrayHasKey('executionsBreakdown', $res);
$this->assertArrayHasKey('bucketsBreakdown', $res);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals(12, count($response['body']));
$this->validateDates($response['body']['network']);
$this->validateDates($response['body']['requests']);
$this->validateDates($response['body']['users']);
$this->assertArrayHasKey('executionsBreakdown', $response['body']);
$this->assertArrayHasKey('bucketsBreakdown', $response['body']);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/users/usage?range=90d',
$consoleHeaders
$this->getConsoleHeaders()
);
$res = $res['body'];
$this->assertEquals('90d', $res['range']);
$this->assertEquals(90, count($res['users']));
$this->assertEquals(90, count($res['sessions']));
$this->assertEquals((self::CREATE / 2), $res['users'][array_key_last($res['users'])]['value']);
$this->assertEquals('90d', $response['body']['range']);
$this->assertEquals(90, count($response['body']['users']));
$this->assertEquals(90, count($response['body']['sessions']));
$this->assertEquals((self::CREATE / 2), $response['body']['users'][array_key_last($response['body']['users'])]['value']);
return [
'projectId' => $projectId,
'headers' => $headers,
'consoleHeaders' => $consoleHeaders,
'requestsTotal' => $requestsTotal,
];
return array_merge($data, [
'requestsTotal' => $requestsTotal
]);
}
/** @depends testUsersStats */
public function testPrepareStorageStats(array $data): array
{
$headers = $data['headers'];
$bucketsTotal = 0;
$requestsTotal = $data['requestsTotal'];
$bucketsTotal = 0;
$storageTotal = 0;
$filesTotal = 0;
for ($i = 0; $i < self::CREATE; $i++) {
$name = uniqid() . ' bucket';
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/storage/buckets',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'bucketId' => 'unique()',
'name' => $name,
@ -188,21 +194,31 @@ class UsageTest extends Scope
],
]
);
$this->assertEquals($name, $res['body']['name']);
$this->assertNotEmpty($res['body']['$id']);
$bucketId = $res['body']['$id'];
$bucketsTotal++;
$requestsTotal++;
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertEquals($name, $response['body']['name']);
$this->assertNotEmpty($response['body']['$id']);
$bucketsTotal += 1;
$requestsTotal += 1;
$bucketId = $response['body']['$id'];
if ($i < (self::CREATE / 2)) {
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_DELETE,
'/storage/buckets/' . $bucketId,
$headers
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
$this->assertEmpty($res['body']);
$requestsTotal++;
$bucketsTotal--;
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEmpty($response['body']);
$requestsTotal += 1;
$bucketsTotal -= 1;
}
}
@ -229,33 +245,44 @@ class UsageTest extends Scope
for ($i = 0; $i < self::CREATE; $i++) {
$file = $files[$i % count($files)];
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/storage/buckets/' . $bucketId . '/files',
array_merge($headers, ['content-type' => 'multipart/form-data']),
array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'fileId' => 'unique()',
'file' => new CURLFile($file['path'], '', $file['name']),
]
);
$this->assertNotEmpty($res['body']['$id']);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$fileSize = $response['body']['sizeOriginal'];
$fileSize = $res['body']['sizeOriginal'];
$storageTotal += $fileSize;
$filesTotal++;
$requestsTotal++;
$filesTotal += 1;
$requestsTotal += 1;
$fileId = $response['body']['$id'];
$fileId = $res['body']['$id'];
if ($i < (self::CREATE / 2)) {
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_DELETE,
'/storage/buckets/' . $bucketId . '/files/' . $fileId,
$headers
array_merge([
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
$this->assertEmpty($res['body']);
$requestsTotal++;
$filesTotal--;
$this->assertEquals(204, $response['headers']['status-code']);
$this->assertEmpty($response['body']);
$requestsTotal += 1;
$filesTotal -= 1;
$storageTotal -= $fileSize;
}
}
@ -283,58 +310,44 @@ class UsageTest extends Scope
sleep(self::WAIT);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/project/usage',
array_merge(
$data['headers'],
$data['consoleHeaders']
),
$this->getConsoleHeaders(),
[
'period' => '1d',
'startDate' => self::getToday(),
'endDate' => self::getTomorrow(),
]
);
$res = $res['body'];
$this->assertEquals(12, count($res));
$this->assertEquals(1, count($res['requests']));
$this->assertEquals($requestsTotal, $res['requests'][array_key_last($res['requests'])]['value']);
$this->validateDates($res['requests']);
$this->assertEquals($storageTotal, $res['filesStorageTotal']);
$this->assertEquals(12, count($response['body']));
$this->assertEquals(1, count($response['body']['requests']));
$this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']);
$this->validateDates($response['body']['requests']);
$this->assertEquals($storageTotal, $response['body']['filesStorageTotal']);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/storage/usage?range=30d',
array_merge(
$data['headers'],
$data['consoleHeaders']
)
$this->getConsoleHeaders()
);
$res = $res['body'];
$this->assertEquals($storageTotal, $res['storage'][array_key_last($res['storage'])]['value']);
$this->validateDates($res['storage']);
$this->assertEquals($bucketsTotal, $res['buckets'][array_key_last($res['buckets'])]['value']);
$this->validateDates($res['buckets']);
$this->assertEquals($filesTotal, $res['files'][array_key_last($res['files'])]['value']);
$this->validateDates($res['files']);
$this->assertEquals($storageTotal, $response['body']['storage'][array_key_last($response['body']['storage'])]['value']);
$this->validateDates($response['body']['storage']);
$this->assertEquals($bucketsTotal, $response['body']['buckets'][array_key_last($response['body']['buckets'])]['value']);
$this->validateDates($response['body']['buckets']);
$this->assertEquals($filesTotal, $response['body']['files'][array_key_last($response['body']['files'])]['value']);
$this->validateDates($response['body']['files']);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/storage/' . $bucketId . '/usage?range=30d',
array_merge(
$data['headers'],
$data['consoleHeaders']
)
$this->getConsoleHeaders()
);
$res = $res['body'];
$this->assertEquals($storageTotal, $res['storage'][array_key_last($res['storage'])]['value']);
$this->assertEquals($filesTotal, $res['files'][array_key_last($res['files'])]['value']);
$data['requestsTotal'] = $requestsTotal;
$this->assertEquals($storageTotal, $response['body']['storage'][array_key_last($response['body']['storage'])]['value']);
$this->assertEquals($filesTotal, $response['body']['files'][array_key_last($response['body']['files'])]['value']);
return $data;
}
@ -342,52 +355,62 @@ class UsageTest extends Scope
/** @depends testStorageStats */
public function testPrepareDatabaseStats(array $data): array
{
$headers = $data['headers'];
$requestsTotal = $data['requestsTotal'];
$databasesTotal = 0;
$collectionsTotal = 0;
$documentsTotal = 0;
for ($i = 0; $i < self::CREATE; $i++) {
$name = uniqid() . ' database';
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/databases',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'databaseId' => 'unique()',
'name' => $name,
]
);
$this->assertEquals($name, $response['body']['name']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals($name, $res['body']['name']);
$this->assertNotEmpty($res['body']['$id']);
$databaseId = $res['body']['$id'];
$requestsTotal += 1;
$databasesTotal += 1;
$requestsTotal++;
$databasesTotal++;
$databaseId = $response['body']['$id'];
if ($i < (self::CREATE / 2)) {
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_DELETE,
'/databases/' . $databaseId,
$headers
array_merge([
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
$this->assertEmpty($res['body']);
$databasesTotal--;
$requestsTotal++;
$this->assertEmpty($response['body']);
$databasesTotal -= 1;
$requestsTotal += 1;
}
}
for ($i = 0; $i < self::CREATE; $i++) {
$name = uniqid() . ' collection';
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'collectionId' => 'unique()',
'name' => $name,
@ -401,29 +424,37 @@ class UsageTest extends Scope
]
);
$this->assertEquals($name, $res['body']['name']);
$this->assertNotEmpty($res['body']['$id']);
$collectionId = $res['body']['$id'];
$this->assertEquals($name, $response['body']['name']);
$this->assertNotEmpty($response['body']['$id']);
$requestsTotal++;
$collectionsTotal++;
$requestsTotal += 1;
$collectionsTotal += 1;
$collectionId = $response['body']['$id'];
if ($i < (self::CREATE / 2)) {
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_DELETE,
'/databases/' . $databaseId . '/collections/' . $collectionId,
$headers
array_merge([
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
$this->assertEmpty($res['body']);
$collectionsTotal--;
$requestsTotal++;
$this->assertEmpty($response['body']);
$collectionsTotal -= 1;
$requestsTotal += 1;
}
}
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes' . '/string',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'key' => 'name',
'size' => 255,
@ -431,38 +462,49 @@ class UsageTest extends Scope
]
);
$this->assertEquals('name', $res['body']['key']);
$requestsTotal++;
$this->assertEquals('name', $response['body']['key']);
$requestsTotal += 1;
sleep(self::WAIT);
for ($i = 0; $i < self::CREATE; $i++) {
$name = uniqid() . ' collection';
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/documents',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'documentId' => 'unique()',
'data' => ['name' => $name]
]
);
$this->assertEquals($name, $res['body']['name']);
$this->assertNotEmpty($res['body']['$id']);
$documentId = $res['body']['$id'];
$requestsTotal++;
$documentsTotal++;
$this->assertEquals($name, $response['body']['name']);
$this->assertNotEmpty($response['body']['$id']);
$requestsTotal += 1;
$documentsTotal += 1;
$documentId = $response['body']['$id'];
if ($i < (self::CREATE / 2)) {
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_DELETE,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId,
$headers
array_merge([
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
$this->assertEmpty($res['body']);
$documentsTotal--;
$requestsTotal++;
$this->assertEmpty($response['body']);
$documentsTotal -= 1;
$requestsTotal += 1;
}
}
@ -480,8 +522,6 @@ class UsageTest extends Scope
#[Retry(count: 1)]
public function testDatabaseStats(array $data): array
{
$projectId = $data['projectId'];
$databaseId = $data['databaseId'];
$collectionId = $data['collectionId'];
$requestsTotal = $data['requestsTotal'];
@ -491,60 +531,58 @@ class UsageTest extends Scope
sleep(self::WAIT);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/project/usage',
$data['consoleHeaders'],
$this->getConsoleHeaders(),
[
'period' => '1d',
'startDate' => self::getToday(),
'endDate' => self::getTomorrow(),
]
);
$res = $res['body'];
$this->assertEquals(12, count($res));
$this->assertEquals(1, count($res['requests']));
$this->assertEquals(1, count($res['network']));
$this->assertEquals($requestsTotal, $res['requests'][array_key_last($res['requests'])]['value']);
$this->validateDates($res['requests']);
$this->assertEquals($databasesTotal, $res['databasesTotal']);
$this->assertEquals($documentsTotal, $res['documentsTotal']);
$this->assertEquals(12, count($response['body']));
$this->assertEquals(1, count($response['body']['requests']));
$this->assertEquals(1, count($response['body']['network']));
$this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']);
$this->validateDates($response['body']['requests']);
$this->assertEquals($databasesTotal, $response['body']['databasesTotal']);
$this->assertEquals($documentsTotal, $response['body']['documentsTotal']);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/databases/usage?range=30d',
$data['consoleHeaders']
$this->getConsoleHeaders()
);
$res = $res['body'];
$this->assertEquals($databasesTotal, $res['databases'][array_key_last($res['databases'])]['value']);
$this->validateDates($res['databases']);
$this->assertEquals($collectionsTotal, $res['collections'][array_key_last($res['collections'])]['value']);
$this->validateDates($res['collections']);
$this->assertEquals($documentsTotal, $res['documents'][array_key_last($res['documents'])]['value']);
$this->validateDates($res['documents']);
$this->assertEquals($databasesTotal, $response['body']['databases'][array_key_last($response['body']['databases'])]['value']);
$this->validateDates($response['body']['databases']);
$this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']);
$this->validateDates($response['body']['collections']);
$this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']);
$this->validateDates($response['body']['documents']);
$res = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/databases/' . $databaseId . '/usage?range=30d',
$data['consoleHeaders']
$this->getConsoleHeaders()
);
$res = $res['body'];
$this->assertEquals($collectionsTotal, $res['collections'][array_key_last($res['collections'])]['value']);
$this->validateDates($res['collections']);
$this->assertEquals($collectionsTotal, $response['body']['collections'][array_key_last($response['body']['collections'])]['value']);
$this->validateDates($response['body']['collections']);
$this->assertEquals($documentsTotal, $res['documents'][array_key_last($res['documents'])]['value']);
$this->validateDates($res['documents']);
$this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']);
$this->validateDates($response['body']['documents']);
$res = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d', $data['consoleHeaders']);
$res = $res['body'];
$response = $this->client->call(
Client::METHOD_GET,
'/databases/' . $databaseId . '/collections/' . $collectionId . '/usage?range=30d',
$this->getConsoleHeaders()
);
$this->assertEquals($documentsTotal, $res['documents'][array_key_last($res['documents'])]['value']);
$this->validateDates($res['documents']);
$data['requestsTotal'] = $requestsTotal;
$this->assertEquals($documentsTotal, $response['body']['documents'][array_key_last($response['body']['documents'])]['value']);
$this->validateDates($response['body']['documents']);
return $data;
}
@ -553,16 +591,17 @@ class UsageTest extends Scope
/** @depends testDatabaseStats */
public function testPrepareFunctionsStats(array $data): array
{
$dateValidator = new DatetimeValidator();
$headers = $data['headers'];
$executionTime = 0;
$executions = 0;
$failures = 0;
$response1 = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/functions',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'functionId' => 'unique()',
'name' => 'Test',
@ -581,18 +620,21 @@ class UsageTest extends Scope
]
);
$functionId = $response1['body']['$id'] ?? '';
$functionId = $response['body']['$id'] ?? '';
$this->assertEquals(201, $response1['headers']['status-code']);
$this->assertNotEmpty($response1['body']['$id']);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$code = realpath(__DIR__ . '/../../resources/functions') . "/php/code.tar.gz";
$this->packageCode('php');
$deployment = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/deployments',
array_merge($headers, ['content-type' => 'multipart/form-data',]),
array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'entrypoint' => 'index.php',
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
@ -600,12 +642,12 @@ class UsageTest extends Scope
]
);
$deploymentId = $deployment['body']['$id'] ?? '';
$deploymentId = $response['body']['$id'] ?? '';
$this->assertEquals(202, $deployment['headers']['status-code']);
$this->assertNotEmpty($deployment['body']['$id']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($deployment['body']['$createdAt']));
$this->assertEquals('index.php', $deployment['body']['entrypoint']);
$this->assertEquals(202, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['$createdAt']));
$this->assertEquals('index.php', $response['body']['entrypoint']);
// Wait for deployment to build.
sleep(self::WAIT + 20);
@ -613,7 +655,10 @@ class UsageTest extends Scope
$response = $this->client->call(
Client::METHOD_PATCH,
'/functions/' . $functionId . '/deployments/' . $deploymentId,
$headers
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
$this->assertEquals(200, $response['headers']['status-code']);
@ -623,74 +668,86 @@ class UsageTest extends Scope
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['$updatedAt']));
$this->assertEquals($deploymentId, $response['body']['deployment']);
$execution = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/executions',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'async' => false,
]
);
$this->assertEquals(201, $execution['headers']['status-code']);
$this->assertNotEmpty($execution['body']['$id']);
$this->assertEquals($functionId, $execution['body']['functionId']);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals($functionId, $response['body']['functionId']);
$executionTime += (int) ($execution['body']['duration'] * 1000);
$executionTime += (int) ($response['body']['duration'] * 1000);
if ($execution['body']['status'] == 'failed') {
$failures++;
} elseif ($execution['body']['status'] == 'completed') {
$executions++;
if ($response['body']['status'] == 'failed') {
$failures += 1;
} elseif ($response['body']['status'] == 'completed') {
$executions += 1;
}
$execution = $this->client->call(
$response = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/executions',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'async' => false,
]
);
$this->assertEquals(201, $execution['headers']['status-code']);
$this->assertNotEmpty($execution['body']['$id']);
$this->assertEquals($functionId, $execution['body']['functionId']);
if ($execution['body']['status'] == 'failed') {
$failures++;
} elseif ($execution['body']['status'] == 'completed') {
$executions++;
}
$executionTime += (int) ($execution['body']['duration'] * 1000);
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals($functionId, $response['body']['functionId']);
$execution = $this->client->call(
if ($response['body']['status'] == 'failed') {
$failures += 1;
} elseif ($response['body']['status'] == 'completed') {
$executions += 1;
}
$executionTime += (int) ($response['body']['duration'] * 1000);
$response = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/executions',
$headers,
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'async' => true,
]
);
$this->assertEquals(202, $execution['headers']['status-code']);
$this->assertNotEmpty($execution['body']['$id']);
$this->assertEquals($functionId, $execution['body']['functionId']);
$this->assertEquals(202, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals($functionId, $response['body']['functionId']);
sleep(self::WAIT);
$execution = $this->client->call(
$response = $this->client->call(
Client::METHOD_GET,
'/functions/' . $functionId . '/executions/' . $execution['body']['$id'],
$headers
'/functions/' . $functionId . '/executions/' . $response['body']['$id'],
array_merge([
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
);
if ($execution['body']['status'] == 'failed') {
$failures++;
} elseif ($execution['body']['status'] == 'completed') {
$executions++;
if ($response['body']['status'] == 'failed') {
$failures += 1;
} elseif ($response['body']['status'] == 'completed') {
$executions += 1;
}
$executionTime += (int) ($execution['body']['duration'] * 1000);
$executionTime += (int) ($response['body']['duration'] * 1000);
return array_merge($data, [
'functionId' => $functionId,
@ -713,7 +770,7 @@ class UsageTest extends Scope
$response = $this->client->call(
Client::METHOD_GET,
'/functions/' . $functionId . '/usage?range=30d',
$data['consoleHeaders']
$this->getConsoleHeaders()
);
$this->assertEquals(200, $response['headers']['status-code']);
@ -725,18 +782,15 @@ class UsageTest extends Scope
$this->assertIsArray($response['body']['buildsTime']);
$this->assertIsArray($response['body']['executions']);
$this->assertIsArray($response['body']['executionsTime']);
$response = $response['body'];
$this->assertEquals($executions, $response['executions'][array_key_last($response['executions'])]['value']);
$this->validateDates($response['executions']);
$this->assertEquals($executionTime, $response['executionsTime'][array_key_last($response['executionsTime'])]['value']);
$this->validateDates($response['executionsTime']);
$this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']);
$this->validateDates($response['body']['executions']);
$this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']);
$this->validateDates($response['body']['executionsTime']);
$response = $this->client->call(
Client::METHOD_GET,
'/functions/usage?range=30d',
$data['consoleHeaders']
$this->getConsoleHeaders()
);
$this->assertEquals(200, $response['headers']['status-code']);
@ -749,15 +803,12 @@ class UsageTest extends Scope
$this->assertIsArray($response['body']['buildsTime']);
$this->assertIsArray($response['body']['executions']);
$this->assertIsArray($response['body']['executionsTime']);
$response = $response['body'];
$this->assertEquals($executions, $response['executions'][array_key_last($response['executions'])]['value']);
$this->validateDates($response['executions']);
$this->assertEquals($executionTime, $response['executionsTime'][array_key_last($response['executionsTime'])]['value']);
$this->validateDates($response['executionsTime']);
$this->assertGreaterThan(0, $response['buildsTime'][array_key_last($response['buildsTime'])]['value']);
$this->validateDates($response['buildsTime']);
$this->assertEquals($executions, $response['body']['executions'][array_key_last($response['body']['executions'])]['value']);
$this->validateDates($response['body']['executions']);
$this->assertEquals($executionTime, $response['body']['executionsTime'][array_key_last($response['body']['executionsTime'])]['value']);
$this->validateDates($response['body']['executionsTime']);
$this->assertGreaterThan(0, $response['body']['buildsTime'][array_key_last($response['body']['buildsTime'])]['value']);
$this->validateDates($response['body']['buildsTime']);
}
public function tearDown(): void

View file

@ -84,8 +84,6 @@ trait ProjectCustom
'rules.read',
'rules.write',
'sessions.write',
'accounts.write',
'accounts.read',
'targets.read',
'targets.write',
'providers.read',

View file

@ -27,7 +27,6 @@ class AccountCustomServerTest extends Scope
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -43,7 +42,6 @@ class AccountCustomServerTest extends Scope
$userId = $response['body']['userId'];
$response = $this->client->call(Client::METHOD_GET, '/users/' . $userId, array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
@ -53,7 +51,6 @@ class AccountCustomServerTest extends Scope
$this->assertNotEmpty($response['body']['accessedAt']);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -67,11 +64,10 @@ class AccountCustomServerTest extends Scope
// already logged in
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-session' => $session,
], $this->getHeaders()), [
]), [
'email' => $email,
'password' => $password,
]);
@ -82,7 +78,6 @@ class AccountCustomServerTest extends Scope
* Test for FAILURE
*/
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -93,7 +88,6 @@ class AccountCustomServerTest extends Scope
$this->assertEquals(401, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -104,7 +98,6 @@ class AccountCustomServerTest extends Scope
$this->assertEquals(401, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/email', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
@ -133,7 +126,6 @@ class AccountCustomServerTest extends Scope
* Test for SUCCESS
*/
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-session' => $session,
@ -154,9 +146,9 @@ class AccountCustomServerTest extends Scope
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
]));
$this->assertEquals(404, $response['headers']['status-code']);
$this->assertEquals(401, $response['headers']['status-code']);
return $data;
}
@ -286,8 +278,7 @@ class AccountCustomServerTest extends Scope
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-session' => $session
],
$this->getHeaders()
]
));
$this->assertEquals($response['headers']['status-code'], 200);

View file

@ -111,8 +111,7 @@ class DatabasesConsoleClientTest extends Scope
$collections = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()));
$this->assertEquals(200, $collections['headers']['status-code']);

View file

@ -114,7 +114,6 @@ class DatabasesCustomClientTest extends Scope
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);

View file

@ -2494,7 +2494,6 @@ class ProjectsConsoleClientTest extends Scope
$response = $this->client->call(Client::METHOD_GET, '/projects/' . $id . '/keys/' . $keyId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $keyId
], $this->getHeaders()), []);
$this->assertEquals(200, $response['headers']['status-code']);