mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 08:58:35 +00:00
Merge branch '1.5.x' into fix-templates-copy-hidden-files
This commit is contained in:
commit
95ef88c80e
20 changed files with 358 additions and 132 deletions
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
|
|
@ -330,7 +330,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
|||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
->label('scope', 'sessions.write')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.hideServer', true)
|
||||
->label('sdk.hide', [APP_PLATFORM_SERVER])
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'createOAuth2Session')
|
||||
->label('sdk.description', '/docs/references/account/create-session-oauth2.md')
|
||||
|
|
@ -400,7 +400,6 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
|||
->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')
|
||||
|
|
@ -1635,8 +1634,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
|||
};
|
||||
|
||||
App::put('/v1/account/sessions/magic-url')
|
||||
->alias('/v1/account/sessions/phone')
|
||||
->desc('Create session (deprecated)')
|
||||
->desc('Update magic URL session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
|
|
@ -1644,8 +1642,39 @@ App::put('/v1/account/sessions/magic-url')
|
|||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.deprecated', true)
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', ['updateMagicURLSession', 'updatePhoneSession'])
|
||||
->label('sdk.method', 'updateMagicURLSession')
|
||||
->label('sdk.description', '/docs/references/account/create-session.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_SESSION)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'ip:{ip},userId:{param-userId}')
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('secret', '', new Text(256), 'Valid verification token.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->action($createSession);
|
||||
|
||||
App::put('/v1/account/sessions/phone')
|
||||
->desc('Update phone session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
->label('audits.event', 'session.create')
|
||||
->label('audits.resource', 'user/{response.userId}')
|
||||
->label('audits.userId', '{response.userId}')
|
||||
->label('sdk.auth', [])
|
||||
->label('sdk.deprecated', true)
|
||||
->label('sdk.namespace', 'account')
|
||||
->label('sdk.method', 'updatePhoneSession')
|
||||
->label('sdk.description', '/docs/references/account/create-session.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
|
|
@ -3918,11 +3947,11 @@ App::put('/v1/account/mfa/challenge')
|
|||
};
|
||||
|
||||
if (!$success && $provider === 'totp') {
|
||||
$backups = $user->getAttribute('mfaBackups', []);
|
||||
$backups = $user->getAttribute('totpBackup', []);
|
||||
if (in_array($otp, $backups)) {
|
||||
$success = true;
|
||||
$backups = array_diff($backups, [$otp]);
|
||||
$user->setAttribute('mfaBackups', $backups);
|
||||
$user->setAttribute('totpBackup', $backups);
|
||||
$dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
}
|
||||
}
|
||||
|
|
@ -3940,6 +3969,9 @@ App::put('/v1/account/mfa/challenge')
|
|||
|
||||
$dbForProject->updateDocument('sessions', $sessionId, $session->setAttribute('factors', $provider, Document::SET_TYPE_APPEND));
|
||||
|
||||
$queueForEvents
|
||||
->setParam('userId', $user->getId());
|
||||
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1616,6 +1616,54 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/attributes/relati
|
|||
$key ??= $relatedCollectionId;
|
||||
$twoWayKey ??= $collectionId;
|
||||
|
||||
$database = Authorization::skip(fn() => $dbForProject->getDocument('databases', $databaseId));
|
||||
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collection = $dbForProject->getDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
$collection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
||||
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$relatedCollectionDocument = $dbForProject->getDocument('database_' . $database->getInternalId(), $relatedCollectionId);
|
||||
$relatedCollection = $dbForProject->getCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollectionDocument->getInternalId());
|
||||
|
||||
if ($relatedCollection->isEmpty()) {
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attributes = $collection->getAttribute('attributes', []);
|
||||
/** @var Document[] $attributes */
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getAttribute('type') !== Database::VAR_RELATIONSHIP) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (\strtolower($attribute->getId()) === \strtolower($key)) {
|
||||
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
if (
|
||||
\strtolower($attribute->getAttribute('options')['twoWayKey']) === \strtolower($twoWayKey) &&
|
||||
$attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
|
||||
) {
|
||||
// Console should provide a unique twoWayKey input!
|
||||
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.');
|
||||
}
|
||||
|
||||
if (
|
||||
$type === Database::RELATION_MANY_TO_MANY &&
|
||||
$attribute->getAttribute('options')['relationType'] === Database::RELATION_MANY_TO_MANY &&
|
||||
$attribute->getAttribute('options')['relatedCollection'] === $relatedCollection->getId()
|
||||
) {
|
||||
throw new Exception(Exception::ATTRIBUTE_ALREADY_EXISTS, 'Creating more than one "manyToMany" relationship on the same collection is currently not permitted.');
|
||||
}
|
||||
}
|
||||
|
||||
$attribute = createAttribute(
|
||||
$databaseId,
|
||||
$collectionId,
|
||||
|
|
|
|||
|
|
@ -420,17 +420,23 @@ App::get('/v1/functions/runtimes')
|
|||
->label('sdk.response.model', Response::MODEL_RUNTIME_LIST)
|
||||
->inject('response')
|
||||
->action(function (Response $response) {
|
||||
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
|
||||
$runtimes = array_map(function ($key) use ($runtimes) {
|
||||
$allowList = \array_filter(\explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
|
||||
|
||||
$allowed = [];
|
||||
foreach ($runtimes as $key => $runtime) {
|
||||
if (!empty($allowList) && !\in_array($key, $allowList)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$runtimes[$key]['$id'] = $key;
|
||||
return $runtimes[$key];
|
||||
}, array_keys($runtimes));
|
||||
$allowed[] = $runtimes[$key];
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => count($runtimes),
|
||||
'runtimes' => $runtimes
|
||||
'total' => count($allowed),
|
||||
'runtimes' => $allowed
|
||||
]), Response::MODEL_RUNTIME_LIST);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -1599,30 +1599,20 @@ App::delete('/v1/users/:userId/mfa/:type')
|
|||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('type', null, new WhiteList(['totp']), 'Type of authenticator.')
|
||||
->param('otp', '', new Text(256), 'Valid verification token.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
->action(function (string $userId, string $type, string $otp, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
->action(function (string $userId, string $type, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents) {
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$success = match ($type) {
|
||||
'totp' => Challenge\TOTP::verify($user, $otp),
|
||||
default => false
|
||||
};
|
||||
|
||||
if (!$success) {
|
||||
throw new Exception(Exception::USER_INVALID_TOKEN);
|
||||
}
|
||||
|
||||
if (!$user->getAttribute('totp')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
|
||||
}
|
||||
if (!$user->getAttribute('totp')) {
|
||||
throw new Exception(Exception::GENERAL_UNKNOWN, 'TOTP not added.');
|
||||
}
|
||||
|
||||
$user
|
||||
->setAttribute('totp', false)
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@
|
|||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.3.*",
|
||||
"utopia-php/messaging": "0.10.*",
|
||||
"utopia-php/migration": "0.3.*",
|
||||
"utopia-php/migration": "0.4.*",
|
||||
"utopia-php/orchestration": "0.9.*",
|
||||
"utopia-php/platform": "0.5.*",
|
||||
"utopia-php/pools": "0.4.*",
|
||||
|
|
|
|||
40
composer.lock
generated
40
composer.lock
generated
|
|
@ -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": "f19c09e7e233fe0f767bf5255fb46b86",
|
||||
"content-hash": "ed4d8b871471fe6a259742c4abe88184",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
|
@ -65,16 +65,16 @@
|
|||
},
|
||||
{
|
||||
"name": "appwrite/appwrite",
|
||||
"version": "10.0.0",
|
||||
"version": "10.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-for-php.git",
|
||||
"reference": "461eedf4efd502dc905c3055f36f0e3583f67390"
|
||||
"reference": "da579af70723cfc117b5af84375bdef117e27312"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/461eedf4efd502dc905c3055f36f0e3583f67390",
|
||||
"reference": "461eedf4efd502dc905c3055f36f0e3583f67390",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/da579af70723cfc117b5af84375bdef117e27312",
|
||||
"reference": "da579af70723cfc117b5af84375bdef117e27312",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -99,10 +99,10 @@
|
|||
"support": {
|
||||
"email": "team@appwrite.io",
|
||||
"issues": "https://github.com/appwrite/sdk-for-php/issues",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/10.0.0",
|
||||
"source": "https://github.com/appwrite/sdk-for-php/tree/10.1.0",
|
||||
"url": "https://appwrite.io/support"
|
||||
},
|
||||
"time": "2023-09-07T23:28:31+00:00"
|
||||
"time": "2023-11-20T09:56:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "appwrite/php-clamav",
|
||||
|
|
@ -1962,20 +1962,20 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.3.6",
|
||||
"version": "0.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "f78273b38bade23db5866e5c7cb5f55427ba82af"
|
||||
"reference": "a72f27bd3dde68752fb185d306c4820e1b8d9657"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/f78273b38bade23db5866e5c7cb5f55427ba82af",
|
||||
"reference": "f78273b38bade23db5866e5c7cb5f55427ba82af",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/a72f27bd3dde68752fb185d306c4820e1b8d9657",
|
||||
"reference": "a72f27bd3dde68752fb185d306c4820e1b8d9657",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"appwrite/appwrite": "10.0.*",
|
||||
"appwrite/appwrite": "10.1.0",
|
||||
"php": "8.*",
|
||||
"utopia-php/cli": "0.*"
|
||||
},
|
||||
|
|
@ -2004,9 +2004,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.3.6"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.4.0"
|
||||
},
|
||||
"time": "2023-11-02T15:13:03+00:00"
|
||||
"time": "2024-02-25T12:35:21+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
|
|
@ -3484,16 +3484,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.25.0",
|
||||
"version": "1.26.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240"
|
||||
"reference": "231e3186624c03d7e7c890ec662b81e6b0405227"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/bd84b629c8de41aa2ae82c067c955e06f1b00240",
|
||||
"reference": "bd84b629c8de41aa2ae82c067c955e06f1b00240",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227",
|
||||
"reference": "231e3186624c03d7e7c890ec662b81e6b0405227",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3525,9 +3525,9 @@
|
|||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.25.0"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0"
|
||||
},
|
||||
"time": "2024-01-04T17:06:16+00:00"
|
||||
"time": "2024-02-23T16:05:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
|
|||
|
|
@ -99,33 +99,26 @@ class Schema
|
|||
/** @var Route $route */
|
||||
|
||||
$namespace = $route->getLabel('sdk.namespace', '');
|
||||
$methods = $route->getLabel('sdk.method', '');
|
||||
$method = $route->getLabel('sdk.method', '');
|
||||
$name = $namespace . \ucfirst($method);
|
||||
|
||||
if (!\is_array($methods)) {
|
||||
$methods = [$methods];
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($methods as $method) {
|
||||
$name = $namespace . \ucfirst($method);
|
||||
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (Mapper::route($utopia, $route, $complexity) as $field) {
|
||||
switch ($route->getMethod()) {
|
||||
case 'GET':
|
||||
$queries[$name] = $field;
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$mutations[$name] = $field;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Unsupported method: {$route->getMethod()}");
|
||||
}
|
||||
foreach (Mapper::route($utopia, $route, $complexity) as $field) {
|
||||
switch ($route->getMethod()) {
|
||||
case 'GET':
|
||||
$queries[$name] = $field;
|
||||
break;
|
||||
case 'POST':
|
||||
case 'PUT':
|
||||
case 'PATCH':
|
||||
case 'DELETE':
|
||||
$mutations[$name] = $field;
|
||||
break;
|
||||
default:
|
||||
throw new \Exception("Unsupported method: {$route->getMethod()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -168,30 +168,35 @@ class Specs extends Action
|
|||
|
||||
foreach ($appRoutes as $key => $method) {
|
||||
foreach ($method as $route) {
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
if ($hide === true || (\is_array($hide) && \in_array($platform, $hide))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/** @var \Utopia\Route $route */
|
||||
$routeSecurity = $route->getLabel('sdk.auth', []);
|
||||
$sdkPlaforms = [];
|
||||
$sdkPlatforms = [];
|
||||
|
||||
foreach ($routeSecurity as $value) {
|
||||
switch ($value) {
|
||||
case APP_AUTH_TYPE_SESSION:
|
||||
$sdkPlaforms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
break;
|
||||
case APP_AUTH_TYPE_KEY:
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_JWT:
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
break;
|
||||
case APP_AUTH_TYPE_ADMIN:
|
||||
$sdkPlaforms[] = APP_PLATFORM_CONSOLE;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CONSOLE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlaforms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlaforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
if (!$route->getLabel('docs', true)) {
|
||||
|
|
@ -210,7 +215,7 @@ class Specs extends Action
|
|||
continue;
|
||||
}
|
||||
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlaforms)) {
|
||||
if ($platform !== APP_PLATFORM_CONSOLE && !\in_array($platforms[$platform], $sdkPlatforms)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Migration\Destinations\Appwrite as DestinationsAppwrite;
|
||||
use Utopia\Migration\Resource;
|
||||
use Utopia\Migration\Source;
|
||||
use Utopia\Migration\Sources\Appwrite;
|
||||
use Utopia\Migration\Sources\Firebase;
|
||||
use Utopia\Migration\Sources\NHost;
|
||||
use Utopia\Migration\Sources\Supabase;
|
||||
use Utopia\Migration\Transfer;
|
||||
use Utopia\Migration\Exception as MigrationException;
|
||||
|
||||
class Migrations extends Action
|
||||
{
|
||||
|
|
@ -285,15 +285,21 @@ class Migrations extends Action
|
|||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
});
|
||||
|
||||
$errors = $transfer->getReport(Resource::STATUS_ERROR);
|
||||
$sourceErrors = $source->getErrors();
|
||||
$destinationErrors = $destination->getErrors();
|
||||
|
||||
if (count($errors) > 0) {
|
||||
if (!empty($sourceErrors) || !empty($destinationErrors)) {
|
||||
$migrationDocument->setAttribute('status', 'failed');
|
||||
$migrationDocument->setAttribute('stage', 'finished');
|
||||
|
||||
$errorMessages = [];
|
||||
foreach ($errors as $error) {
|
||||
$errorMessages[] = "Failed to transfer resource '{$error['id']}:{$error['resource']}' with message '{$error['message']}'";
|
||||
foreach ($sourceErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceType()}:{$error->getResourceId()}' from source with message: '{$error->getMessage()}'";
|
||||
}
|
||||
foreach ($destinationErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceType()}:{$error->getResourceId()}' to destination with message: '{$error->getMessage()}'";
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
|
|
@ -318,21 +324,32 @@ class Migrations extends Action
|
|||
}
|
||||
|
||||
if ($transfer) {
|
||||
$errors = $transfer->getReport(Resource::STATUS_ERROR);
|
||||
$sourceErrors = $source->getErrors();
|
||||
$destinationErrors = $destination->getErrors();
|
||||
|
||||
if (count($errors) > 0) {
|
||||
$migrationDocument->setAttribute('status', 'failed');
|
||||
$migrationDocument->setAttribute('stage', 'finished');
|
||||
$migrationDocument->setAttribute('errors', $errors);
|
||||
$errorMessages = [];
|
||||
foreach ($sourceErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while fetching '{$error->getResourceType()}:{$error->getResourceId()}' from source with message '{$error->getMessage()}'";
|
||||
}
|
||||
foreach ($destinationErrors as $error) {
|
||||
/** @var MigrationException $error */
|
||||
$errorMessages[] = "Error occurred while pushing '{$error->getResourceType()}:{$error->getResourceId()}' to destination with message '{$error->getMessage()}'";
|
||||
}
|
||||
|
||||
$migrationDocument->setAttribute('errors', $errorMessages);
|
||||
}
|
||||
} finally {
|
||||
if ($migrationDocument) {
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
}
|
||||
if ($tempAPIKey) {
|
||||
$this->removeAPIKey($tempAPIKey);
|
||||
}
|
||||
if ($migrationDocument) {
|
||||
$this->updateMigrationDocument($migrationDocument, $projectDocument);
|
||||
|
||||
if ($migrationDocument->getAttribute('status', '') == 'failed') {
|
||||
throw new Exception(implode("\n", $migrationDocument->getAttribute('errors', [])));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,18 +120,10 @@ class OpenAPI3 extends Format
|
|||
foreach ($this->routes as $route) {
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
|
||||
|
||||
if ($hide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', [\uniqid()]);
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
|
|
@ -156,12 +148,8 @@ class OpenAPI3 extends Format
|
|||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
if (!$route->getLabel('sdk.hideServer', false)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
}
|
||||
if (!$route->getLabel('sdk.hideClient', false)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$temp = [
|
||||
|
|
@ -175,6 +163,7 @@ class OpenAPI3 extends Format
|
|||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'deprecated' => $route->getLabel('sdk.deprecated', false),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
|
|
|
|||
|
|
@ -118,18 +118,9 @@ class Swagger2 extends Format
|
|||
/** @var \Utopia\Route $route */
|
||||
$url = \str_replace('/v1', '', $route->getPath());
|
||||
$scope = $route->getLabel('scope', '');
|
||||
$hide = $route->getLabel('sdk.hide', false);
|
||||
$consumes = [$route->getLabel('sdk.request.type', 'application/json')];
|
||||
|
||||
if ($hide) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', [\uniqid()]);
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
|
||||
$method = $route->getLabel('sdk.method', \uniqid());
|
||||
$desc = (!empty($route->getLabel('sdk.description', ''))) ? \realpath(__DIR__ . '/../../../../' . $route->getLabel('sdk.description', '')) : null;
|
||||
$produces = $route->getLabel('sdk.response.type', null);
|
||||
$model = $route->getLabel('sdk.response.model', 'none');
|
||||
|
|
@ -154,8 +145,8 @@ class Swagger2 extends Format
|
|||
}
|
||||
|
||||
if (empty($routeSecurity)) {
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
$sdkPlatforms[] = APP_PLATFORM_SERVER;
|
||||
$sdkPlatforms[] = APP_PLATFORM_CLIENT;
|
||||
}
|
||||
|
||||
$temp = [
|
||||
|
|
@ -171,6 +162,7 @@ class Swagger2 extends Format
|
|||
'weight' => $route->getOrder(),
|
||||
'cookies' => $route->getLabel('sdk.cookies', false),
|
||||
'type' => $route->getLabel('sdk.methodType', ''),
|
||||
'deprecated' => $route->getLabel('sdk.deprecated', false),
|
||||
'demo' => Template::fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')) . '/' . Template::fromCamelCaseToDash($method) . '.md',
|
||||
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
|
||||
'rate-limit' => $route->getLabel('abuse-limit', 0),
|
||||
|
|
|
|||
|
|
@ -25,10 +25,7 @@ class Request extends UtopiaRequest
|
|||
$parameters = parent::getParams();
|
||||
|
||||
if (self::hasFilter() && self::hasRoute()) {
|
||||
$method = self::getRoute()->getLabel('sdk.method', ['unknown']);
|
||||
if (\is_array($method)) {
|
||||
$method = $method[0];
|
||||
}
|
||||
$method = self::getRoute()->getLabel('sdk.method', 'unknown');
|
||||
$endpointIdentifier = self::getRoute()->getLabel('sdk.namespace', 'unknown') . '.' . $method;
|
||||
$parameters = self::getFilter()->parse($parameters, $endpointIdentifier);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2247,12 +2247,10 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertEmpty($response['body']['secret']);
|
||||
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['expire']));
|
||||
|
||||
\sleep(10);
|
||||
|
||||
$smsRequest = $this->getLastRequest();
|
||||
|
||||
return \array_merge($data, [
|
||||
'token' => $smsRequest['data']['secret']
|
||||
'token' => \substr($smsRequest['data']['message'], 0, 6)
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
|
@ -316,6 +317,164 @@ class DatabasesCustomClientTest extends Scope
|
|||
$this->assertEquals('restrict', $collection1RelationAttribute['onDelete']);
|
||||
}
|
||||
|
||||
public function testRelationshipSameTwoWayKey(): void
|
||||
{
|
||||
$database = $this->client->call(Client::METHOD_POST, '/databases', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
], [
|
||||
'databaseId' => ID::unique(),
|
||||
'name' => 'Same two way key'
|
||||
]);
|
||||
|
||||
$databaseId = $database['body']['$id'];
|
||||
|
||||
$collection1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'c1',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
$collection2 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'collectionId' => ID::unique(),
|
||||
'name' => 'c2',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::create(Role::user($this->getUser()['$id'])),
|
||||
Permission::read(Role::user($this->getUser()['$id'])),
|
||||
Permission::update(Role::user($this->getUser()['$id'])),
|
||||
Permission::delete(Role::user($this->getUser()['$id'])),
|
||||
]
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_ONE,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr1',
|
||||
'twoWayKey' => 'same_key'
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(202, $relation['headers']['status-code']);
|
||||
$this->assertEquals('same_key', $relation['body']['twoWayKey']);
|
||||
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr2',
|
||||
'twoWayKey' => 'same_key'
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(409, $relation['body']['code']);
|
||||
$this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']);
|
||||
|
||||
// twoWayKey is null TwoWayKey is default
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr3',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(202, $relation['headers']['status-code']);
|
||||
$this->assertArrayHasKey('twoWayKey', $relation['body']);
|
||||
|
||||
// twoWayKey is null, TwoWayKey is default, second POST
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => false,
|
||||
'onDelete' => 'cascade',
|
||||
'key' => 'attr4',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals('Attribute with the requested key already exists. Attribute keys must be unique, try again with a different key.', $relation['body']['message']);
|
||||
$this->assertEquals(409, $relation['body']['code']);
|
||||
|
||||
// RelationshipManyToMany
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_MANY_TO_MANY,
|
||||
'twoWay' => true,
|
||||
'onDelete' => 'setNull',
|
||||
'key' => 'songs',
|
||||
'twoWayKey' => 'playlist',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(202, $relation['headers']['status-code']);
|
||||
$this->assertArrayHasKey('twoWayKey', $relation['body']);
|
||||
|
||||
// Second RelationshipManyToMany on Same collections
|
||||
$relation = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collection1['body']['$id'] . '/attributes/relationship', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'relatedCollectionId' => $collection2['body']['$id'],
|
||||
'type' => Database::RELATION_MANY_TO_MANY,
|
||||
'twoWay' => true,
|
||||
'onDelete' => 'setNull',
|
||||
'key' => 'songs2',
|
||||
'twoWayKey' => 'playlist2',
|
||||
]);
|
||||
|
||||
\sleep(2);
|
||||
|
||||
$this->assertEquals(409, $relation['body']['code']);
|
||||
$this->assertEquals('Creating more than one "manyToMany" relationship on the same collection is currently not permitted.', $relation['body']['message']);
|
||||
}
|
||||
|
||||
public function testUpdateWithoutRelationPermission(): void
|
||||
{
|
||||
$userId = $this->getUser()['$id'];
|
||||
|
|
|
|||
Loading…
Reference in a new issue