mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 08:28:42 +00:00
Merge remote-tracking branch 'origin/1.3.x' into feat-db-update-migrations
This commit is contained in:
commit
f6bd9d4150
109 changed files with 5846 additions and 740 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -1,4 +1,4 @@
|
|||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = 2.2.0
|
||||
branch = feat-databases-v2
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
|
|||
RUN npm ci
|
||||
RUN npm run build
|
||||
|
||||
FROM appwrite/base:0.1.0 as final
|
||||
FROM appwrite/base:0.2.2 as final
|
||||
|
||||
LABEL maintainer="team@appwrite.io"
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
</p>
|
||||
|
||||
<!-- [](https://travis-ci.com/appwrite/appwrite) -->
|
||||
|
||||
[](https://appwrite.io/company/careers)
|
||||
[](https://hacktoberfest.appwrite.io)
|
||||
[](https://appwrite.io/discord?r=Github)
|
||||
[](https://github.com/appwrite/appwrite/actions)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
<!-- [](https://travis-ci.com/appwrite/appwrite) -->
|
||||
|
||||
[](https://appwrite.io/company/careers)
|
||||
[](https://hacktoberfest.appwrite.io)
|
||||
[](https://appwrite.io/discord?r=Github)
|
||||
[](https://github.com/appwrite/appwrite/actions)
|
||||
|
|
|
|||
|
|
@ -346,6 +346,16 @@ $collections = [
|
|||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('options'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 16384,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
|
@ -1865,6 +1875,17 @@ $collections = [
|
|||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('prefs'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => new \stdClass(),
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
|
|
|||
|
|
@ -413,6 +413,11 @@ return [
|
|||
'description' => 'Remote document is newer than local.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::DOCUMENT_DELETE_RESTRICTED => [
|
||||
'name' => Exception::DOCUMENT_DELETE_RESTRICTED,
|
||||
'description' => 'Document cannot be deleted because it is referenced by another document.',
|
||||
'code' => 403,
|
||||
],
|
||||
|
||||
/** Attributes */
|
||||
Exception::ATTRIBUTE_NOT_FOUND => [
|
||||
|
|
@ -422,12 +427,12 @@ return [
|
|||
],
|
||||
Exception::ATTRIBUTE_UNKNOWN => [
|
||||
'name' => Exception::ATTRIBUTE_UNKNOWN,
|
||||
'description' => 'The attribute required for the index could not be found. Please confirm all your attributes are in the <span class="tag">available</span> state.',
|
||||
'description' => 'The attribute required for the index could not be found. Please confirm all your attributes are in the available state.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_NOT_AVAILABLE => [
|
||||
'name' => Exception::ATTRIBUTE_NOT_AVAILABLE,
|
||||
'description' => 'The requested attribute is not yet <span class="tag">available</span>. Please try again later.',
|
||||
'description' => 'The requested attribute is not yet available. Please try again later.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_FORMAT_UNSUPPORTED => [
|
||||
|
|
@ -437,7 +442,7 @@ return [
|
|||
],
|
||||
Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED => [
|
||||
'name' => Exception::ATTRIBUTE_DEFAULT_UNSUPPORTED,
|
||||
'description' => 'Default values cannot be set for <span class="tag">array</span> and <span class="tag">required</span> attributes.',
|
||||
'description' => 'Default values cannot be set for array or required attributes.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_ALREADY_EXISTS => [
|
||||
|
|
@ -455,6 +460,11 @@ return [
|
|||
'description' => 'The attribute value is invalid. Please check the type, range and value of the attribute.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::ATTRIBUTE_TYPE_INVALID => [
|
||||
'name' => Exception::ATTRIBUTE_TYPE_INVALID,
|
||||
'description' => 'The attribute type is invalid.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Indexes */
|
||||
Exception::INDEX_NOT_FOUND => [
|
||||
|
|
@ -554,6 +564,11 @@ return [
|
|||
'description' => 'Domain verification for the requested domain has failed.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::DOMAIN_TARGET_INVALID => [
|
||||
'name' => Exception::DOMAIN_TARGET_INVALID,
|
||||
'description' => 'Your Appwrite instance is not publicly accessible. Please check the _APP_DOMAIN_TARGET environment variable of your Appwrite server.',
|
||||
'code' => 501,
|
||||
],
|
||||
Exception::GRAPHQL_NO_QUERY => [
|
||||
'name' => Exception::GRAPHQL_NO_QUERY,
|
||||
'description' => 'Param "query" is not optional.',
|
||||
|
|
|
|||
|
|
@ -183,13 +183,16 @@ return [
|
|||
],
|
||||
],
|
||||
'create' => [
|
||||
'$description' => 'This event triggers when a bucket is created.'
|
||||
'$description' => 'This event triggers when a team is created.'
|
||||
],
|
||||
'delete' => [
|
||||
'$description' => 'This event triggers when a bucket is deleted.',
|
||||
'$description' => 'This event triggers when a team is deleted.',
|
||||
],
|
||||
'update' => [
|
||||
'$description' => 'This event triggers when a bucket is updated.',
|
||||
'$description' => 'This event triggers when a team is updated.',
|
||||
'prefs' => [
|
||||
'$description' => 'This event triggers when a team\'s preferences are updated.',
|
||||
],
|
||||
]
|
||||
],
|
||||
'functions' => [
|
||||
|
|
|
|||
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
|
|
@ -1 +1 @@
|
|||
Subproject commit 5e2a40c1e397bd341a432698c9d76a3f96315841
|
||||
Subproject commit 8d6b58467fe13635ba75bb62f2d94280f8b7ceb8
|
||||
|
|
@ -1411,7 +1411,7 @@ App::get('/v1/account/logs')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('locale')
|
||||
|
|
@ -1557,7 +1557,7 @@ App::patch('/v1/account/password')
|
|||
->label('sdk.description', '/docs/references/account/update-password.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_ACCOUNT)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->label('sdk.offline.model', '/account')
|
||||
->label('sdk.offline.key', 'current')
|
||||
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be at least 8 chars.', false, ['project', 'passwordsDictionary'])
|
||||
|
|
@ -1761,7 +1761,7 @@ App::patch('/v1/account/status')
|
|||
->label('sdk.description', '/docs/references/account/update-status.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_ACCOUNT)
|
||||
->label('sdk.response.model', Response::MODEL_USER)
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -25,7 +25,6 @@ use Appwrite\Task\Validator\Cron;
|
|||
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Executions;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Functions;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Variables;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
|
@ -33,7 +32,6 @@ use Utopia\Database\DateTime;
|
|||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
|
@ -63,7 +61,7 @@ App::post('/v1/functions')
|
|||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new CustomId(), 'Function 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('name', '', new Text(128), 'Function name. Max length: 128 chars.')
|
||||
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.')
|
||||
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
|
||||
->param('runtime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Execution runtime.')
|
||||
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
|
|
@ -125,7 +123,7 @@ App::get('/v1/functions')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FUNCTION_LIST)
|
||||
->param('queries', [], new Functions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Functions::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Functions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Functions::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -440,7 +438,7 @@ App::put('/v1/functions/:functionId')
|
|||
->label('sdk.response.model', Response::MODEL_FUNCTION)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('name', '', new Text(128), 'Function name. Max length: 128 chars.')
|
||||
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.')
|
||||
->param('execute', [], new Roles(APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings with execution roles. By default no user is granted with any execute permissions. [learn more about permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 64 characters long.', true)
|
||||
->param('events', [], new ArrayList(new ValidatorEvent(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Events list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.', true)
|
||||
->param('schedule', '', new Cron(), 'Schedule CRON syntax.', true)
|
||||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Maximum execution time in seconds.', true)
|
||||
|
|
@ -601,8 +599,8 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
->label('sdk.response.model', Response::MODEL_DEPLOYMENT)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('entrypoint', '', new Text('1028'), 'Entrypoint File.')
|
||||
->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', false)
|
||||
->param('activate', false, new Boolean(true), 'Automatically activate the deployment when it is finished building.', false)
|
||||
->param('code', [], new File(), 'Gzip file with your code package. When used with the Appwrite CLI, pass the path to your code directory, and the CLI will automatically package your code. Use a path that is within the current directory.', skipValidation: true)
|
||||
->param('activate', false, new Boolean(true), 'Automatically activate the deployment when it is finished building.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -791,7 +789,7 @@ App::get('/v1/functions/:functionId/deployments')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_DEPLOYMENT_LIST)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Deployments(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Deployments::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -1223,7 +1221,7 @@ App::get('/v1/functions/:functionId/executions')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_EXECUTION_LIST)
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('queries', [], new Executions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Executions::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Executions(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Executions::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -1348,7 +1346,7 @@ App::post('/v1/functions/:functionId/variables')
|
|||
->label('sdk.response.model', Response::MODEL_VARIABLE)
|
||||
->param('functionId', '', new UID(), 'Function unique ID.', false)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, string $key, string $value, Response $response, Database $dbForProject) {
|
||||
|
|
@ -1464,7 +1462,7 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
->param('functionId', '', new UID(), 'Function unique ID.', false)
|
||||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ App::get('/v1/graphql')
|
|||
->label('sdk.response.model', Response::MODEL_ANY)
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-time', 60)
|
||||
->param('query', '', new Text(0), 'The query to execute.')
|
||||
->param('query', '', new Text(0, 0), 'The query to execute.')
|
||||
->param('operationName', '', new Text(256), 'The name of the operation to execute.', true)
|
||||
->param('variables', '', new Text(0), 'The JSON encoded variables to use in the query.', true)
|
||||
->inject('request')
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ use Utopia\Database\Helpers\Permission;
|
|||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Registry\Registry;
|
||||
|
|
@ -184,7 +184,7 @@ App::get('/v1/projects')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PROJECT_LIST)
|
||||
->param('queries', [], new Projects(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Projects::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Projects(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Projects::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
|
|
@ -1402,7 +1402,7 @@ App::post('/v1/projects/:projectId/domains')
|
|||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
if (!$target->isKnown() || $target->isTest()) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.');
|
||||
throw new Exception(Exception::DOMAIN_TARGET_INVALID, 'Unreachable CNAME target (' . $target->get() . '). Please check the _APP_DOMAIN_TARGET environment variable of your Appwrite server.');
|
||||
}
|
||||
|
||||
$domain = new Domain($domain);
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ App::get('/v1/storage/buckets')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_BUCKET_LIST)
|
||||
->param('queries', [], new Buckets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Buckets::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Buckets(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Buckets::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -348,7 +348,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->label('sdk.response.model', Response::MODEL_FILE)
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new CustomId(), 'File 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('file', [], new File(), 'Binary file.', false)
|
||||
->param('file', [], new File(), 'Binary file.', skipValidation: true)
|
||||
->param('permissions', null, new Permissions(APP_LIMIT_ARRAY_PARAMS_SIZE, [Database::PERMISSION_READ, Database::PERMISSION_UPDATE, Database::PERMISSION_DELETE, Database::PERMISSION_WRITE]), 'An array of permission strings. By default, only the current user is granted all permissions. [Learn more about permissions](/docs/permissions).', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
|
@ -680,7 +680,7 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_FILE_LIST)
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](/docs/server/storage#createBucket).')
|
||||
->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Files(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Files::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ use Utopia\Database\Validator\Authorization;
|
|||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
App::post('/v1/teams')
|
||||
->desc('Create Team')
|
||||
|
|
@ -75,6 +76,7 @@ App::post('/v1/teams')
|
|||
],
|
||||
'name' => $name,
|
||||
'total' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
'prefs' => new \stdClass(),
|
||||
'search' => implode(' ', [$teamId, $name]),
|
||||
])));
|
||||
|
||||
|
|
@ -133,7 +135,7 @@ App::get('/v1/teams')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM_LIST)
|
||||
->label('sdk.offline.model', '/teams')
|
||||
->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Teams::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Teams(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Teams::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -198,8 +200,36 @@ App::get('/v1/teams/:teamId')
|
|||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/prefs')
|
||||
->desc('Get Team Preferences')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'getPrefs')
|
||||
->label('sdk.description', '/docs/references/teams/get-team-prefs.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PREFERENCES)
|
||||
->label('sdk.offline.model', '/teams/{teamId}/prefs')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $teamId, Response $response, Database $dbForProject) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$prefs = $team->getAttribute('prefs', new \stdClass());
|
||||
|
||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::put('/v1/teams/:teamId')
|
||||
->desc('Update Team')
|
||||
->desc('Update Name')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].update')
|
||||
->label('scope', 'teams.write')
|
||||
|
|
@ -207,8 +237,8 @@ App::put('/v1/teams/:teamId')
|
|||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'update')
|
||||
->label('sdk.description', '/docs/references/teams/update-team.md')
|
||||
->label('sdk.method', 'updateName')
|
||||
->label('sdk.description', '/docs/references/teams/update-team-name.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_TEAM)
|
||||
|
|
@ -241,6 +271,42 @@ App::put('/v1/teams/:teamId')
|
|||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
|
||||
App::put('/v1/teams/:teamId/prefs')
|
||||
->desc('Update Preferences')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].update.prefs')
|
||||
->label('scope', 'teams.write')
|
||||
->label('audits.event', 'team.update')
|
||||
->label('audits.resource', 'team/{response.$id}')
|
||||
->label('audits.userId', '{response.$id}')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'teams')
|
||||
->label('sdk.method', 'updatePrefs')
|
||||
->label('sdk.description', '/docs/references/teams/update-team-prefs.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PREFERENCES)
|
||||
->label('sdk.offline.model', '/teams/{teamId}/prefs')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('prefs', '', new Assoc(), 'Prefs key-value JSON object.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->action(function (string $teamId, array $prefs, Response $response, Database $dbForProject, Event $events) {
|
||||
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('prefs', $prefs));
|
||||
|
||||
$events->setParam('teamId', $team->getId());
|
||||
|
||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::delete('/v1/teams/:teamId')
|
||||
->desc('Delete Team')
|
||||
->groups(['api', 'teams'])
|
||||
|
|
@ -541,7 +607,7 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
->label('sdk.response.model', Response::MODEL_MEMBERSHIP_LIST)
|
||||
->label('sdk.offline.model', '/teams/{teamId}/memberships')
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -936,7 +1002,7 @@ App::get('/v1/teams/:teamId/logs')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ App::get('/v1/users')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USER_LIST)
|
||||
->param('queries', [], new Users(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true)
|
||||
->param('queries', [], new Users(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Users::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -557,7 +557,7 @@ App::get('/v1/users/:userId/logs')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ App::get('/.well-known/acme-challenge')
|
|||
$uriChunks = \explode('/', $request->getURI());
|
||||
$token = $uriChunks[\count($uriChunks) - 1];
|
||||
|
||||
$validator = new Text(100, [
|
||||
$validator = new Text(100, allowList: [
|
||||
...Text::NUMBERS,
|
||||
...Text::ALPHABET_LOWER,
|
||||
...Text::ALPHABET_UPPER,
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use Utopia\Validator\Text;
|
|||
use Utopia\Storage\Validator\File;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
App::get('/v1/mock/tests/foo')
|
||||
->desc('Get Foo')
|
||||
|
|
@ -428,6 +429,21 @@ App::get('/v1/mock/tests/general/empty')
|
|||
$response->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/mock/tests/general/nullable')
|
||||
->desc('Nullable Test')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
|
||||
->label('sdk.namespace', 'general')
|
||||
->label('sdk.method', 'nullable')
|
||||
->label('sdk.description', 'Mock a nullable parameter.')
|
||||
->label('sdk.mock', true)
|
||||
->param('required', '', new Text(100), 'Sample string param')
|
||||
->param('nullable', '', new Nullable(new Text(100)), 'Sample string param')
|
||||
->param('optional', '', new Text(100), 'Sample string param', true)
|
||||
->action(function (string $required, string $nullable, ?string $optional) {
|
||||
});
|
||||
|
||||
/** Endpoint to test if required headers are sent from the SDK */
|
||||
App::get('/v1/mock/tests/general/headers')
|
||||
->desc('Get headers')
|
||||
|
|
|
|||
|
|
@ -165,14 +165,14 @@ App::post('/v1/runtimes')
|
|||
->desc("Create a new runtime server")
|
||||
->param('runtimeId', '', new Text(64), 'Unique runtime ID.')
|
||||
->param('source', '', new Text(0), 'Path to source files.')
|
||||
->param('destination', '', new Text(0), 'Destination folder to store build files into.', true)
|
||||
->param('destination', '', new Text(0, 0), 'Destination folder to store build files into.', true)
|
||||
->param('vars', [], new Assoc(), 'Environment Variables required for the build.')
|
||||
->param('commands', [], new ArrayList(new Text(1024), 100), 'Commands required to build the container. Maximum of 100 commands are allowed, each 1024 characters long.')
|
||||
->param('runtime', '', new Text(128), 'Runtime for the cloud function.')
|
||||
->param('baseImage', '', new Text(128), 'Base image name of the runtime.')
|
||||
->param('entrypoint', '', new Text(256), 'Entrypoint of the code file.', true)
|
||||
->param('remove', false, new Boolean(), 'Remove a runtime after execution.')
|
||||
->param('workdir', '', new Text(256), 'Working directory.', true)
|
||||
->param('workdir', '', new Text(256, 0), 'Working directory.', true)
|
||||
->inject('orchestrationPool')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
|
|
@ -459,7 +459,7 @@ App::post('/v1/execution')
|
|||
->desc('Create an execution')
|
||||
->param('runtimeId', '', new Text(64), 'The runtimeID to execute.')
|
||||
->param('vars', [], new Assoc(), 'Environment variables required for the build.')
|
||||
->param('data', '', new Text(8192), 'Data to be forwarded to the function, this is user specified.', true)
|
||||
->param('data', '', new Text(8192, 0), 'Data to be forwarded to the function, this is user specified.', true)
|
||||
->param('timeout', 15, new Range(1, (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900)), 'Function maximum execution time in seconds.')
|
||||
->inject('activeRuntimes')
|
||||
->inject('response')
|
||||
|
|
|
|||
|
|
@ -295,7 +295,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$log->addExtra('line', $th->getLine());
|
||||
$log->addExtra('trace', $th->getTraceAsString());
|
||||
$log->addExtra('detailedTrace', $th->getTrace());
|
||||
$log->addExtra('roles', Authorization::$roles);
|
||||
$log->addExtra('roles', Authorization::getRoles());
|
||||
|
||||
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
|
||||
$log->setAction($action);
|
||||
|
|
|
|||
25
app/init.php
25
app/init.php
|
|
@ -53,7 +53,7 @@ use Utopia\Database\Database;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Structure;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Messaging\Adapters\SMS\Mock;
|
||||
|
|
@ -285,12 +285,23 @@ Database::addFilter(
|
|||
return null;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForAttributes()),
|
||||
]);
|
||||
$attributes = $database->find('attributes', [
|
||||
Query::equal('collectionInternalId', [$document->getInternalId()]),
|
||||
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
|
||||
Query::limit($database->getLimitForAttributes()),
|
||||
]);
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
|
||||
$options = $attribute->getAttribute('options');
|
||||
foreach ($options as $key => $value) {
|
||||
$attribute->setAttribute($key, $value);
|
||||
}
|
||||
$attribute->removeAttribute('options');
|
||||
}
|
||||
}
|
||||
|
||||
return $attributes;
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Resque\Worker;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
|
|
@ -89,16 +92,51 @@ class DatabaseV1 extends Worker
|
|||
$format = $attribute->getAttribute('format', '');
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
$filters = $attribute->getAttribute('filters', []);
|
||||
$options = $attribute->getAttribute('options', []);
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
throw new Exception('Failed to create Attribute');
|
||||
switch ($type) {
|
||||
case Database::VAR_RELATIONSHIP:
|
||||
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
|
||||
if ($relatedCollection->isEmpty()) {
|
||||
throw new Exception('Collection not found');
|
||||
}
|
||||
|
||||
if (
|
||||
!$dbForProject->createRelationship(
|
||||
collection: 'database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(),
|
||||
relatedCollection: 'database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId(),
|
||||
type: $options['relationType'],
|
||||
twoWay: $options['twoWay'],
|
||||
id: $key,
|
||||
twoWayKey: $options['twoWayKey'],
|
||||
onDelete: $options['onDelete'],
|
||||
)
|
||||
) {
|
||||
throw new Exception('Failed to create Attribute');
|
||||
}
|
||||
|
||||
if ($options['twoWay']) {
|
||||
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
|
||||
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'available'));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!$dbForProject->createAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
throw new Exception('Failed to create Attribute');
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available'));
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'failed'));
|
||||
|
||||
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
|
||||
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
|
||||
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'failed'));
|
||||
}
|
||||
} finally {
|
||||
$target = Realtime::fromPayload(
|
||||
// Pass first, most verbose event pattern
|
||||
|
|
@ -121,6 +159,10 @@ class DatabaseV1 extends Worker
|
|||
);
|
||||
}
|
||||
|
||||
if ($type === Database::VAR_RELATIONSHIP && $options['twoWay']) {
|
||||
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
|
||||
}
|
||||
|
||||
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
}
|
||||
|
||||
|
|
@ -129,6 +171,7 @@ class DatabaseV1 extends Worker
|
|||
* @param Document $collection
|
||||
* @param Document $attribute
|
||||
* @param string $projectId
|
||||
* @throws Throwable
|
||||
*/
|
||||
protected function deleteAttribute(Document $database, Document $collection, Document $attribute, string $projectId): void
|
||||
{
|
||||
|
|
@ -143,19 +186,43 @@ class DatabaseV1 extends Worker
|
|||
$collectionId = $collection->getId();
|
||||
$key = $attribute->getAttribute('key', '');
|
||||
$status = $attribute->getAttribute('status', '');
|
||||
$type = $attribute->getAttribute('type', '');
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
$options = $attribute->getAttribute('options', []);
|
||||
$relatedAttribute = new Document();
|
||||
$relatedCollection = new Document();
|
||||
// possible states at this point:
|
||||
// - available: should not land in queue; controller flips these to 'deleting'
|
||||
// - processing: hasn't finished creating
|
||||
// - deleting: was available, in deletion queue for first time
|
||||
// - failed: attribute was never created
|
||||
// - stuck: attribute was available but cannot be removed
|
||||
|
||||
try {
|
||||
if ($status !== 'failed' && !$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
if ($status !== 'failed') {
|
||||
if ($type === Database::VAR_RELATIONSHIP) {
|
||||
if ($options['twoWay']) {
|
||||
$relatedCollection = $dbForProject->getDocument('database_' . $database->getInternalId(), $options['relatedCollection']);
|
||||
if ($relatedCollection->isEmpty()) {
|
||||
throw new Exception(Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
$relatedAttribute = $dbForProject->getDocument('attributes', $database->getInternalId() . '_' . $relatedCollection->getInternalId() . '_' . $options['twoWayKey']);
|
||||
}
|
||||
|
||||
if (!$dbForProject->deleteRelationship('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
|
||||
$dbForProject->updateDocument('attributes', $relatedAttribute->getId(), $relatedAttribute->setAttribute('status', 'stuck'));
|
||||
throw new Exception('Failed to delete Relationship');
|
||||
}
|
||||
} elseif (!$dbForProject->deleteAttribute('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId(), $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
}
|
||||
}
|
||||
|
||||
$dbForProject->deleteDocument('attributes', $attribute->getId());
|
||||
|
||||
if (!$relatedAttribute->isEmpty()) {
|
||||
$dbForProject->deleteDocument('attributes', $relatedAttribute->getId());
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::error($th->getMessage());
|
||||
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'stuck'));
|
||||
|
|
@ -199,8 +266,8 @@ class DatabaseV1 extends Worker
|
|||
// array_values wraps array_diff to reindex array keys
|
||||
// when found attribute is removed from array
|
||||
$attributes = \array_values(\array_diff($attributes, [$attributes[$found]]));
|
||||
$lengths = \array_values(\array_diff($lengths, [$lengths[$found]]));
|
||||
$orders = \array_values(\array_diff($orders, [$orders[$found]]));
|
||||
$lengths = \array_values(\array_diff($lengths, isset($lengths[$found]) ? [$lengths[$found]] : []));
|
||||
$orders = \array_values(\array_diff($orders, isset($orders[$found]) ? [$orders[$found]] : []));
|
||||
|
||||
if (empty($attributes)) {
|
||||
$dbForProject->deleteDocument('indexes', $index->getId());
|
||||
|
|
@ -235,6 +302,11 @@ class DatabaseV1 extends Worker
|
|||
|
||||
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $collectionId);
|
||||
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId());
|
||||
|
||||
if (!$relatedCollection->isEmpty() && !$relatedAttribute->isEmpty()) {
|
||||
$dbForProject->deleteCachedDocument('database_' . $database->getInternalId(), $relatedCollection->getId());
|
||||
$dbForProject->deleteCachedCollection('database_' . $database->getInternalId() . '_collection_' . $relatedCollection->getInternalId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -203,6 +203,21 @@ class DeletesV1 extends Worker
|
|||
|
||||
$dbForProject = $this->getProjectDB($projectId);
|
||||
|
||||
$relationships = \array_filter(
|
||||
$document->getAttribute('attributes'),
|
||||
fn ($attribute) => $attribute['type'] === Database::VAR_RELATIONSHIP
|
||||
);
|
||||
|
||||
foreach ($relationships as $relationship) {
|
||||
if (!$relationship['twoWay']) {
|
||||
continue;
|
||||
}
|
||||
$relatedCollection = $dbForProject->getDocument('database_' . $databaseInternalId, $relationship['relatedCollection']);
|
||||
$dbForProject->deleteDocument('attributes', $databaseInternalId . '_' . $relatedCollection->getInternalId() . '_' . $relationship['twoWayKey']);
|
||||
$dbForProject->deleteCachedDocument('database_' . $databaseInternalId, $relatedCollection->getId());
|
||||
$dbForProject->deleteCachedCollection('database_' . $databaseInternalId . '_collection_' . $relatedCollection->getInternalId());
|
||||
}
|
||||
|
||||
$dbForProject->deleteCollection('database_' . $databaseInternalId . '_collection_' . $document->getInternalId());
|
||||
|
||||
$this->deleteByGroup('attributes', [
|
||||
|
|
|
|||
|
|
@ -43,27 +43,27 @@
|
|||
"ext-sockets": "*",
|
||||
"appwrite/php-clamav": "1.1.*",
|
||||
"appwrite/php-runtimes": "0.11.*",
|
||||
"utopia-php/abuse": "0.19.*",
|
||||
"utopia-php/abuse": "0.24.*",
|
||||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.21.*",
|
||||
"utopia-php/audit": "0.25.*",
|
||||
"utopia-php/cache": "0.8.*",
|
||||
"utopia-php/cli": "0.13.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
"utopia-php/database": "0.31.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/database": "0.35.*",
|
||||
"utopia-php/domains": "1.1.*",
|
||||
"utopia-php/framework": "0.26.*",
|
||||
"utopia-php/framework": "0.28.*",
|
||||
"utopia-php/image": "0.5.*",
|
||||
"utopia-php/locale": "0.4.*",
|
||||
"utopia-php/logger": "0.3.*",
|
||||
"utopia-php/messaging": "0.1.*",
|
||||
"utopia-php/orchestration": "0.6.*",
|
||||
"utopia-php/preloader": "0.2.*",
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.13.*",
|
||||
"utopia-php/storage": "0.14.*",
|
||||
"utopia-php/swoole": "0.5.*",
|
||||
"utopia-php/websocket": "0.1.*",
|
||||
"resque/php-resque": "1.3.6",
|
||||
"matomo/device-detector": "6.0.0",
|
||||
"matomo/device-detector": "6.0.*",
|
||||
"dragonmantank/cron-expression": "3.3.1",
|
||||
"influxdb/influxdb-php": "1.15.2",
|
||||
"phpmailer/phpmailer": "6.6.0",
|
||||
|
|
|
|||
303
composer.lock
generated
303
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": "3e00aa37bea907b7dcca7f912402a392",
|
||||
"content-hash": "164a9bd37203b233d663d709394feb07",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
|
@ -693,16 +693,16 @@
|
|||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.4.3",
|
||||
"version": "2.4.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "67c26b443f348a51926030c83481b85718457d3d"
|
||||
"reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/67c26b443f348a51926030c83481b85718457d3d",
|
||||
"reference": "67c26b443f348a51926030c83481b85718457d3d",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
|
||||
"reference": "3cf1b6d4f0c820a2cf8bcaec39fc698f3443b5cf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -792,7 +792,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.4.3"
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.4.4"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -808,7 +808,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-10-26T14:07:24+00:00"
|
||||
"time": "2023-03-09T13:19:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "influxdb/influxdb-php",
|
||||
|
|
@ -1000,16 +1000,16 @@
|
|||
},
|
||||
{
|
||||
"name": "matomo/device-detector",
|
||||
"version": "6.0.0",
|
||||
"version": "6.0.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/matomo-org/device-detector.git",
|
||||
"reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9"
|
||||
"reference": "ce5ef5e6776c16af306d38e20674973f072e05ed"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/7fc2af3af62bd69e6e3404d561e371a83c112be9",
|
||||
"reference": "7fc2af3af62bd69e6e3404d561e371a83c112be9",
|
||||
"url": "https://api.github.com/repos/matomo-org/device-detector/zipball/ce5ef5e6776c16af306d38e20674973f072e05ed",
|
||||
"reference": "ce5ef5e6776c16af306d38e20674973f072e05ed",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1065,7 +1065,7 @@
|
|||
"source": "https://github.com/matomo-org/matomo",
|
||||
"wiki": "https://dev.matomo.org/"
|
||||
},
|
||||
"time": "2022-04-11T09:58:17+00:00"
|
||||
"time": "2023-01-16T08:18:02+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mongodb/mongodb",
|
||||
|
|
@ -1372,25 +1372,25 @@
|
|||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"reference": "cb6ce4845ce34a8ad9e68117c10ee90a29919eba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
"dev-master": "1.1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
|
@ -1419,9 +1419,9 @@
|
|||
"response"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/php-fig/http-message/tree/master"
|
||||
"source": "https://github.com/php-fig/http-message/tree/1.1"
|
||||
},
|
||||
"time": "2016-08-06T14:39:51+00:00"
|
||||
"time": "2023-04-04T09:50:52+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/log",
|
||||
|
|
@ -1658,16 +1658,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.2.0",
|
||||
"version": "v3.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3"
|
||||
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/1ee04c65529dea5d8744774d474e7cbd2f1206d3",
|
||||
"reference": "1ee04c65529dea5d8744774d474e7cbd2f1206d3",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
|
||||
"reference": "e2d1534420bd723d0ef5aec58a22c5fe60ce6f5e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -1705,7 +1705,7 @@
|
|||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.2.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -1721,7 +1721,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-11-25T10:21:52+00:00"
|
||||
"time": "2023-03-01T10:25:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-php80",
|
||||
|
|
@ -1808,23 +1808,23 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/abuse",
|
||||
"version": "0.19.0",
|
||||
"version": "0.24.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/abuse.git",
|
||||
"reference": "419b6e2e0a5dec35ea83a25758df9cd129b6c412"
|
||||
"reference": "403641f16a53b81ac40b91111a86e5672da49e8c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/419b6e2e0a5dec35ea83a25758df9cd129b6c412",
|
||||
"reference": "419b6e2e0a5dec35ea83a25758df9cd129b6c412",
|
||||
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/403641f16a53b81ac40b91111a86e5672da49e8c",
|
||||
"reference": "403641f16a53b81ac40b91111a86e5672da49e8c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-curl": "*",
|
||||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/database": "0.31.*"
|
||||
"utopia-php/database": "0.35.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
|
|
@ -1851,9 +1851,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/abuse/issues",
|
||||
"source": "https://github.com/utopia-php/abuse/tree/0.19.0"
|
||||
"source": "https://github.com/utopia-php/abuse/tree/0.24.0"
|
||||
},
|
||||
"time": "2023-02-26T03:28:48+00:00"
|
||||
"time": "2023-04-11T05:31:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
|
|
@ -1912,28 +1912,26 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/audit",
|
||||
"version": "0.21.0",
|
||||
"version": "0.25.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/audit.git",
|
||||
"reference": "7f9783a14718c82570c6effb35a3cb42c30b13a2"
|
||||
"reference": "adc209f2e16878e5468f0b9cfd9f7f7ab497db31"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/7f9783a14718c82570c6effb35a3cb42c30b13a2",
|
||||
"reference": "7f9783a14718c82570c6effb35a3cb42c30b13a2",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/adc209f2e16878e5468f0b9cfd9f7f7ab497db31",
|
||||
"reference": "adc209f2e16878e5468f0b9cfd9f7f7ab497db31",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/database": "0.31.*"
|
||||
"utopia-php/database": "0.35.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
|
@ -1955,9 +1953,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/audit/issues",
|
||||
"source": "https://github.com/utopia-php/audit/tree/0.21.0"
|
||||
"source": "https://github.com/utopia-php/audit/tree/0.25.0"
|
||||
},
|
||||
"time": "2023-02-26T03:28:30+00:00"
|
||||
"time": "2023-04-11T05:31:15+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
|
|
@ -2114,33 +2112,37 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "0.31.0",
|
||||
"version": "0.35.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "61f9f4743a317f1d78558a5f981adf74e7fdc931"
|
||||
"reference": "f162c142fd61753c4b413b15c3c4041f3cd00bb2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/61f9f4743a317f1d78558a5f981adf74e7fdc931",
|
||||
"reference": "61f9f4743a317f1d78558a5f981adf74e7fdc931",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/f162c142fd61753c4b413b15c3c4041f3cd00bb2",
|
||||
"reference": "f162c142fd61753c4b413b15c3c4041f3cd00bb2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/cache": "0.8.*",
|
||||
"utopia-php/framework": "0.*.*",
|
||||
"utopia-php/mongo": "0.0.2"
|
||||
"utopia-php/mongo": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-mongodb": "*",
|
||||
"ext-redis": "*",
|
||||
"fakerphp/faker": "^1.14",
|
||||
"laravel/pint": "1.4.*",
|
||||
"mongodb/mongodb": "1.8.0",
|
||||
"pcov/clobber": "^2.0",
|
||||
"phpstan/phpstan": "1.10.*",
|
||||
"phpunit/phpunit": "^9.4",
|
||||
"rregeer/phpunit-coverage-check": "^0.3.1",
|
||||
"swoole/ide-helper": "4.8.0",
|
||||
"utopia-php/cli": "^0.14.0",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
"utopia-php/cli": "^0.14.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
|
|
@ -2152,7 +2154,7 @@
|
|||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A simple library to manage application persistency using multiple database adapters",
|
||||
"description": "A simple library to manage application persistence using multiple database adapters",
|
||||
"keywords": [
|
||||
"database",
|
||||
"framework",
|
||||
|
|
@ -2162,9 +2164,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/0.31.0"
|
||||
"source": "https://github.com/utopia-php/database/tree/0.35.0"
|
||||
},
|
||||
"time": "2023-02-23T09:49:44+00:00"
|
||||
"time": "2023-04-11T04:02:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/domains",
|
||||
|
|
@ -2222,16 +2224,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/framework",
|
||||
"version": "0.26.0",
|
||||
"version": "0.28.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/framework.git",
|
||||
"reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e"
|
||||
"reference": "7f22c556fc5991e54e5811a68fb39809b21bda55"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e",
|
||||
"reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e",
|
||||
"url": "https://api.github.com/repos/utopia-php/framework/zipball/7f22c556fc5991e54e5811a68fb39809b21bda55",
|
||||
"reference": "7f22c556fc5991e54e5811a68fb39809b21bda55",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2260,9 +2262,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/framework/issues",
|
||||
"source": "https://github.com/utopia-php/framework/tree/0.26.0"
|
||||
"source": "https://github.com/utopia-php/framework/tree/0.28.1"
|
||||
},
|
||||
"time": "2023-01-13T08:14:43+00:00"
|
||||
"time": "2023-03-02T08:16:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/image",
|
||||
|
|
@ -2470,16 +2472,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
"version": "0.0.2",
|
||||
"version": "0.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/mongo.git",
|
||||
"reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e"
|
||||
"reference": "b6dfb31b93c07c59b8bbd62a3b52e3b97a407c09"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/mongo/zipball/62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e",
|
||||
"reference": "62f9a9c0201af91b6d0dd4f0aa8a335ec9b56a1e",
|
||||
"url": "https://api.github.com/repos/utopia-php/mongo/zipball/b6dfb31b93c07c59b8bbd62a3b52e3b97a407c09",
|
||||
"reference": "b6dfb31b93c07c59b8bbd62a3b52e3b97a407c09",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -2524,9 +2526,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/mongo/issues",
|
||||
"source": "https://github.com/utopia-php/mongo/tree/0.0.2"
|
||||
"source": "https://github.com/utopia-php/mongo/tree/0.2.0"
|
||||
},
|
||||
"time": "2022-11-08T11:58:46+00:00"
|
||||
"time": "2023-03-22T10:44:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/orchestration",
|
||||
|
|
@ -2690,26 +2692,30 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/storage",
|
||||
"version": "0.13.2",
|
||||
"version": "0.14.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage.git",
|
||||
"reference": "ad1c00f24ca56e73888acc2af3deee4919b1194b"
|
||||
"reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/ad1c00f24ca56e73888acc2af3deee4919b1194b",
|
||||
"reference": "ad1c00f24ca56e73888acc2af3deee4919b1194b",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/eda6651ac16884dc2a79ecb984ea591ba1ed498c",
|
||||
"reference": "eda6651ac16884dc2a79ecb984ea591ba1ed498c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-brotli": "*",
|
||||
"ext-fileinfo": "*",
|
||||
"ext-lz4": "*",
|
||||
"ext-snappy": "*",
|
||||
"ext-zlib": "*",
|
||||
"ext-zstd": "*",
|
||||
"php": ">=8.0",
|
||||
"utopia-php/framework": "0.*.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/pint": "1.2.*",
|
||||
"phpunit/phpunit": "^9.3",
|
||||
"vimeo/psalm": "4.0.1"
|
||||
},
|
||||
|
|
@ -2723,12 +2729,6 @@
|
|||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Eldad Fux",
|
||||
"email": "eldad@appwrite.io"
|
||||
}
|
||||
],
|
||||
"description": "A simple Storage library to manage application storage",
|
||||
"keywords": [
|
||||
"framework",
|
||||
|
|
@ -2739,9 +2739,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/storage/issues",
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.13.2"
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.14.0"
|
||||
},
|
||||
"time": "2022-12-20T11:11:35+00:00"
|
||||
"time": "2023-03-15T00:16:34+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
|
|
@ -3086,6 +3086,49 @@
|
|||
},
|
||||
"time": "2023-02-03T05:44:59+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/deprecations",
|
||||
"version": "v1.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/deprecations.git",
|
||||
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
|
||||
"reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^9",
|
||||
"phpunit/phpunit": "^7.5|^8.5|^9.5",
|
||||
"psr/log": "^1|^2|^3"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
|
||||
"homepage": "https://www.doctrine-project.org/",
|
||||
"support": {
|
||||
"issues": "https://github.com/doctrine/deprecations/issues",
|
||||
"source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
|
||||
},
|
||||
"time": "2022-05-02T15:47:09+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/instantiator",
|
||||
"version": "1.5.0",
|
||||
|
|
@ -3282,16 +3325,16 @@
|
|||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.11.0",
|
||||
"version": "1.11.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614"
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614",
|
||||
"reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3329,7 +3372,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.0"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.11.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3337,20 +3380,20 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-03-03T13:19:32+00:00"
|
||||
"time": "2023-03-08T13:26:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
"version": "v4.15.3",
|
||||
"version": "v4.15.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039"
|
||||
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039",
|
||||
"reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039",
|
||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
|
||||
"reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3391,9 +3434,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3"
|
||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
|
||||
},
|
||||
"time": "2023-01-16T22:05:37+00:00"
|
||||
"time": "2023-03-05T19:49:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phar-io/manifest",
|
||||
|
|
@ -3618,24 +3661,27 @@
|
|||
},
|
||||
{
|
||||
"name": "phpdocumentor/type-resolver",
|
||||
"version": "1.6.2",
|
||||
"version": "1.7.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpDocumentor/TypeResolver.git",
|
||||
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d"
|
||||
"reference": "dfc078e8af9c99210337325ff5aa152872c98714"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
|
||||
"reference": "48f445a408c131e38cab1c235aa6d2bb7a0bb20d",
|
||||
"url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/dfc078e8af9c99210337325ff5aa152872c98714",
|
||||
"reference": "dfc078e8af9c99210337325ff5aa152872c98714",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"doctrine/deprecations": "^1.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"phpdocumentor/reflection-common": "^2.0"
|
||||
"phpdocumentor/reflection-common": "^2.0",
|
||||
"phpstan/phpdoc-parser": "^1.13"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-tokenizer": "*",
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/extension-installer": "^1.1",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpstan/phpstan-phpunit": "^1.1",
|
||||
|
|
@ -3667,9 +3713,9 @@
|
|||
"description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpDocumentor/TypeResolver/issues",
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.6.2"
|
||||
"source": "https://github.com/phpDocumentor/TypeResolver/tree/1.7.1"
|
||||
},
|
||||
"time": "2022-10-14T12:47:21+00:00"
|
||||
"time": "2023-03-27T19:02:04+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpspec/prophecy",
|
||||
|
|
@ -3740,17 +3786,62 @@
|
|||
"time": "2023-02-02T15:41:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.25",
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "1.18.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954"
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954",
|
||||
"reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
|
||||
"reference": "22dcdfd725ddf99583bfe398fc624ad6c5004a0f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||
"phpstan/extension-installer": "^1.0",
|
||||
"phpstan/phpstan": "^1.5",
|
||||
"phpstan/phpstan-phpunit": "^1.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.0",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"symfony/process": "^5.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PHPStan\\PhpDocParser\\": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"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.18.1"
|
||||
},
|
||||
"time": "2023-04-07T11:51:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
"version": "9.2.26",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||
"reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -3772,8 +3863,8 @@
|
|||
"phpunit/phpunit": "^9.3"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pcov": "*",
|
||||
"ext-xdebug": "*"
|
||||
"ext-pcov": "PHP extension that provides line coverage",
|
||||
"ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
|
|
@ -3806,7 +3897,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25"
|
||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -3814,7 +3905,7 @@
|
|||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2023-02-25T05:32:00+00:00"
|
||||
"time": "2023-03-06T12:58:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-file-iterator",
|
||||
|
|
|
|||
|
|
@ -343,7 +343,6 @@ services:
|
|||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
#- ./vendor/utopia-php/database:/usr/src/code/vendor/utopia-php/database
|
||||
depends_on:
|
||||
- redis
|
||||
- mariadb
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
Sends the user an email with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. When the user clicks the link in the email, the user is redirected back to the URL you provided with the secret key and userId values attached to the URL query string. Use the query string parameters to submit a request to the [PUT /account/sessions/magic-url](/docs/client/account#accountUpdateMagicURLSession) endpoint to complete the login process. The link sent to the user's email address is valid for 1 hour. If you are on a mobile device you can leave the URL parameter empty, so that the login completion will be handled by your Appwrite instance by default.
|
||||
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication#limits).
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication-security#limits).
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
Sends the user an SMS with a secret key for creating a session. If the provided user ID has not be registered, a new user will be created. Use the returned user ID and secret and submit a request to the [PUT /account/sessions/phone](/docs/client/account#accountUpdatePhoneSession) endpoint to complete the login process. The secret sent to the user's phone is valid for 15 minutes.
|
||||
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication#limits).
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication-security#limits).
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
Allow the user to login into their account by providing a valid email and password combination. This route will create a new session for the user.
|
||||
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication#limits).
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication-security#limits).
|
||||
|
|
@ -2,4 +2,4 @@ Allow the user to login to their account using the OAuth2 provider of their choi
|
|||
|
||||
If there is already an active session, the new session will be attached to the logged-in account. If there are no active sessions, the server will attempt to look for a user with the same email address as the email received from the OAuth2 provider and attach the new session to the existing user. If no matching user is found - the server will create a new user.
|
||||
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication#limits).
|
||||
A user is limited to 10 active sessions at a time by default. [Learn more about session limits](/docs/authentication-security#limits).
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Create relationship attribute. [Learn more about relationship attributes](docs/databases-relationships#relationship-attributes).
|
||||
1
docs/references/databases/update-email-attribute.md
Normal file
1
docs/references/databases/update-email-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update an email attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-enum-attribute.md
Normal file
1
docs/references/databases/update-enum-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update an enum attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-float-attribute.md
Normal file
1
docs/references/databases/update-float-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a float attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-integer-attribute.md
Normal file
1
docs/references/databases/update-integer-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update an integer attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-ip-attribute.md
Normal file
1
docs/references/databases/update-ip-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update an ip attribute. Changing the `default` value will not update already existing documents.
|
||||
|
|
@ -0,0 +1 @@
|
|||
Update relationship attribute. [Learn more about relationship attributes](docs/databases-relationships#relationship-attributes).
|
||||
1
docs/references/databases/update-string-attribute.md
Normal file
1
docs/references/databases/update-string-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a string attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/databases/update-url-attribute.md
Normal file
1
docs/references/databases/update-url-attribute.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update an url attribute. Changing the `default` value will not update already existing documents.
|
||||
1
docs/references/teams/get-team-prefs.md
Normal file
1
docs/references/teams/get-team-prefs.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Get the team's shared preferences by its unique ID. If a preference doesn't need to be shared by all team members, prefer storing them in [user preferences](/docs/client/account#accountGetPrefs).
|
||||
1
docs/references/teams/update-team-name.md
Normal file
1
docs/references/teams/update-team-name.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update the team's name by its unique ID.
|
||||
1
docs/references/teams/update-team-prefs.md
Normal file
1
docs/references/teams/update-team-prefs.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update the team's preferences by its unique ID. The object you pass is stored as is and replaces any previous value. The maximum allowed prefs size is 64kB and throws an error if exceeded.
|
||||
|
|
@ -1 +0,0 @@
|
|||
Update a team using its ID. Only members with the owner role can update the team.
|
||||
|
|
@ -18,7 +18,7 @@ In order to capture the Appwrite OAuth callback url, the following activity need
|
|||
<application ...>
|
||||
....
|
||||
<!-- Add this inside the <application> tag, along side the existing <activity> tags -->
|
||||
<activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" >
|
||||
<activity android:exported="true" android:name="com.linusu.flutter_web_auth_2.CallbackActivity" >
|
||||
<intent-filter android:label="flutter_web_auth_2">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ As the name implies, `param()` is used to define a request parameter.
|
|||
|
||||
```php
|
||||
App::get('/v1/account/logs')
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/databases#querying-documents). Only supported methods are limit and offset', true)
|
||||
->param('queries', [], new Queries(new Limit(), new Offset()), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
```
|
||||
|
||||
### 6. inject
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ class Exception extends \Exception
|
|||
public const DOCUMENT_MISSING_PAYLOAD = 'document_missing_payload';
|
||||
public const DOCUMENT_ALREADY_EXISTS = 'document_already_exists';
|
||||
public const DOCUMENT_UPDATE_CONFLICT = 'document_update_conflict';
|
||||
public const DOCUMENT_DELETE_RESTRICTED = 'document_delete_restricted';
|
||||
|
||||
/** Attribute */
|
||||
public const ATTRIBUTE_NOT_FOUND = 'attribute_not_found';
|
||||
|
|
@ -146,6 +147,7 @@ class Exception extends \Exception
|
|||
public const ATTRIBUTE_ALREADY_EXISTS = 'attribute_already_exists';
|
||||
public const ATTRIBUTE_LIMIT_EXCEEDED = 'attribute_limit_exceeded';
|
||||
public const ATTRIBUTE_VALUE_INVALID = 'attribute_value_invalid';
|
||||
public const ATTRIBUTE_TYPE_INVALID = 'attribute_type_invalid';
|
||||
|
||||
/** Indexes */
|
||||
public const INDEX_NOT_FOUND = 'index_not_found';
|
||||
|
|
@ -179,6 +181,7 @@ class Exception extends \Exception
|
|||
public const DOMAIN_NOT_FOUND = 'domain_not_found';
|
||||
public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists';
|
||||
public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed';
|
||||
public const DOMAIN_TARGET_INVALID = 'domain_target_invalid';
|
||||
|
||||
/** GraphqQL */
|
||||
public const GRAPHQL_NO_QUERY = 'graphql_no_query';
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use GraphQL\Type\Definition\UnionType;
|
|||
use Utopia\App;
|
||||
use Utopia\Route;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Mapper
|
||||
{
|
||||
|
|
@ -109,9 +110,6 @@ class Mapper
|
|||
'type' => $parameterType,
|
||||
'description' => $parameter['description'],
|
||||
];
|
||||
if ($parameter['optional']) {
|
||||
$params[$name]['defaultValue'] = $parameter['default'];
|
||||
}
|
||||
}
|
||||
|
||||
$field = [
|
||||
|
|
@ -224,6 +222,12 @@ class Mapper
|
|||
? \call_user_func_array($validator, $utopia->getResources($injections))
|
||||
: $validator;
|
||||
|
||||
$isNullable = $validator instanceof Nullable;
|
||||
|
||||
if ($isNullable) {
|
||||
$validator = $validator->getValidator();
|
||||
}
|
||||
|
||||
switch ((!empty($validator)) ? $validator::class : '') {
|
||||
case 'Appwrite\Network\Validator\CNAME':
|
||||
case 'Appwrite\Task\Validator\Cron':
|
||||
|
|
@ -294,7 +298,7 @@ class Mapper
|
|||
break;
|
||||
}
|
||||
|
||||
if ($required) {
|
||||
if ($required && !$isNullable) {
|
||||
$type = Type::nonNull($type);
|
||||
}
|
||||
|
||||
|
|
@ -404,6 +408,8 @@ class Mapper
|
|||
return static::model('AttributeBoolean');
|
||||
case 'datetime':
|
||||
return static::model('AttributeDatetime');
|
||||
case 'relationship':
|
||||
return static::model('AttributeRelationship');
|
||||
}
|
||||
|
||||
throw new Exception('Unknown attribute implementation');
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Appwrite\Utopia\Response\Model;
|
|||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class OpenAPI3 extends Format
|
||||
{
|
||||
|
|
@ -284,6 +285,13 @@ class OpenAPI3 extends Format
|
|||
}
|
||||
}
|
||||
|
||||
$isNullable = $validator instanceof Nullable;
|
||||
|
||||
if ($isNullable) {
|
||||
/** @var Nullable $validator */
|
||||
$validator = $validator->getValidator();
|
||||
}
|
||||
|
||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||
case 'Utopia\Validator\Text':
|
||||
$node['schema']['type'] = $validator->getType();
|
||||
|
|
@ -449,6 +457,10 @@ class OpenAPI3 extends Format
|
|||
if ($node['x-global'] ?? false) {
|
||||
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-global'] = true;
|
||||
}
|
||||
|
||||
if ($isNullable) {
|
||||
$body['content'][$consumes[0]]['schema']['properties'][$name]['x-nullable'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
$url = \str_replace(':' . $name, '{' . $name . '}', $url);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Appwrite\Utopia\Response\Model;
|
|||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Nullable;
|
||||
|
||||
class Swagger2 extends Format
|
||||
{
|
||||
|
|
@ -285,6 +286,13 @@ class Swagger2 extends Format
|
|||
}
|
||||
}
|
||||
|
||||
$isNullable = $validator instanceof Nullable;
|
||||
|
||||
if ($isNullable) {
|
||||
/** @var Nullable $validator */
|
||||
$validator = $validator->getValidator();
|
||||
}
|
||||
|
||||
switch ((!empty($validator)) ? \get_class($validator) : '') {
|
||||
case 'Utopia\Validator\Text':
|
||||
$node['type'] = $validator->getType();
|
||||
|
|
@ -448,6 +456,10 @@ class Swagger2 extends Format
|
|||
$body['schema']['properties'][$name]['x-global'] = true;
|
||||
}
|
||||
|
||||
if ($isNullable) {
|
||||
$body['schema']['properties'][$name]['x-nullable'] = true;
|
||||
}
|
||||
|
||||
if (\array_key_exists('items', $node)) {
|
||||
$body['schema']['properties'][$name]['items'] = $node['items'];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,26 +10,26 @@ use Utopia\Database\Query;
|
|||
class IndexedQueries extends Queries
|
||||
{
|
||||
/**
|
||||
* @var Document[]
|
||||
* @var array<Document>
|
||||
*/
|
||||
protected $attributes = [];
|
||||
protected array $attributes = [];
|
||||
|
||||
/**
|
||||
* @var Document[]
|
||||
* @var array<Document>
|
||||
*/
|
||||
protected $indexes = [];
|
||||
protected array $indexes = [];
|
||||
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* This Queries Validator filters indexes for only available indexes
|
||||
*
|
||||
* @param Document[] $attributes
|
||||
* @param Document[] $indexes
|
||||
* @param array<Document> $attributes
|
||||
* @param array<Document> $indexes
|
||||
* @param Base ...$validators
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct($attributes = [], $indexes = [], Base ...$validators)
|
||||
public function __construct(array $attributes = [], array $indexes = [], Base ...$validators)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
|
||||
|
|
@ -55,33 +55,6 @@ class IndexedQueries extends Queries
|
|||
parent::__construct(...$validators);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if indexed array $indexes matches $queries
|
||||
*
|
||||
* @param array $indexes
|
||||
* @param array $queries
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function arrayMatch(array $indexes, array $queries): bool
|
||||
{
|
||||
// Check the count of indexes first for performance
|
||||
if (count($queries) !== count($indexes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort them for comparison, the order is not important here anymore.
|
||||
sort($indexes, SORT_STRING);
|
||||
sort($queries, SORT_STRING);
|
||||
|
||||
// Only matching arrays will have equal diffs in both directions
|
||||
if (array_diff_assoc($indexes, $queries) !== array_diff_assoc($queries, $indexes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
|
|
@ -111,41 +84,26 @@ class IndexedQueries extends Queries
|
|||
}
|
||||
|
||||
$grouped = Query::groupByType($queries);
|
||||
/** @var Query[] */ $filters = $grouped['filters'];
|
||||
/** @var string[] */ $orderAttributes = $grouped['orderAttributes'];
|
||||
$filters = $grouped['filters'];
|
||||
|
||||
// Check filter queries for exact index match
|
||||
if (count($filters) > 0) {
|
||||
$filtersByAttribute = [];
|
||||
foreach ($filters as $filter) {
|
||||
$filtersByAttribute[$filter->getAttribute()] = $filter->getMethod();
|
||||
}
|
||||
foreach ($filters as $filter) {
|
||||
if ($filter->getMethod() === Query::TYPE_SEARCH) {
|
||||
$matched = false;
|
||||
|
||||
$found = null;
|
||||
foreach ($this->indexes as $index) {
|
||||
if (
|
||||
$index->getAttribute('type') === Database::INDEX_FULLTEXT
|
||||
&& $index->getAttribute('attributes') === [$filter->getAttribute()]
|
||||
) {
|
||||
$matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->indexes as $index) {
|
||||
if ($this->arrayMatch($index->getAttribute('attributes'), array_keys($filtersByAttribute))) {
|
||||
$found = $index;
|
||||
if (!$matched) {
|
||||
$this->message = "Searching by attribute \"{$filter->getAttribute()}\" requires a fulltext index.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$found) {
|
||||
$this->message = 'Index not found: ' . implode(",", array_keys($filtersByAttribute));
|
||||
return false;
|
||||
}
|
||||
|
||||
// search method requires fulltext index
|
||||
if (in_array(Query::TYPE_SEARCH, array_values($filtersByAttribute)) && $found['type'] !== Database::INDEX_FULLTEXT) {
|
||||
$this->message = 'Search method requires fulltext index: ' . implode(",", array_keys($filtersByAttribute));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check order attributes for exact index match
|
||||
$validator = new OrderAttributes($this->attributes, $this->indexes, true);
|
||||
if (count($orderAttributes) > 0 && !$validator->isValid($orderAttributes)) {
|
||||
$this->message = $validator->getDescription();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ class Queries extends Validator
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $message = 'Invalid queries';
|
||||
protected string $message = 'Invalid queries';
|
||||
|
||||
/**
|
||||
* @var Base[]
|
||||
* @var array<Base>
|
||||
*/
|
||||
protected $validators;
|
||||
protected array $validators;
|
||||
|
||||
/**
|
||||
* Queries constructor
|
||||
|
|
@ -57,41 +57,35 @@ class Queries extends Validator
|
|||
if (!$query instanceof Query) {
|
||||
try {
|
||||
$query = Query::parse($query);
|
||||
} catch (\Throwable $th) {
|
||||
$this->message = 'Invalid query: ${query}';
|
||||
} catch (\Throwable) {
|
||||
$this->message = "Invalid query: {$query}";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$method = $query->getMethod();
|
||||
$methodType = '';
|
||||
switch ($method) {
|
||||
case Query::TYPE_LIMIT:
|
||||
$methodType = Base::METHOD_TYPE_LIMIT;
|
||||
break;
|
||||
case Query::TYPE_OFFSET:
|
||||
$methodType = Base::METHOD_TYPE_OFFSET;
|
||||
break;
|
||||
case Query::TYPE_CURSORAFTER:
|
||||
case Query::TYPE_CURSORBEFORE:
|
||||
$methodType = Base::METHOD_TYPE_CURSOR;
|
||||
break;
|
||||
case Query::TYPE_ORDERASC:
|
||||
case Query::TYPE_ORDERDESC:
|
||||
$methodType = Base::METHOD_TYPE_ORDER;
|
||||
break;
|
||||
case Query::TYPE_EQUAL:
|
||||
case Query::TYPE_NOTEQUAL:
|
||||
case Query::TYPE_LESSER:
|
||||
case Query::TYPE_LESSEREQUAL:
|
||||
case Query::TYPE_GREATER:
|
||||
case Query::TYPE_GREATEREQUAL:
|
||||
case Query::TYPE_SEARCH:
|
||||
$methodType = Base::METHOD_TYPE_FILTER;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
$methodType = match ($method) {
|
||||
Query::TYPE_SELECT => Base::METHOD_TYPE_SELECT,
|
||||
Query::TYPE_LIMIT => Base::METHOD_TYPE_LIMIT,
|
||||
Query::TYPE_OFFSET => Base::METHOD_TYPE_OFFSET,
|
||||
Query::TYPE_CURSORAFTER,
|
||||
Query::TYPE_CURSORBEFORE => Base::METHOD_TYPE_CURSOR,
|
||||
Query::TYPE_ORDERASC,
|
||||
Query::TYPE_ORDERDESC => Base::METHOD_TYPE_ORDER,
|
||||
Query::TYPE_EQUAL,
|
||||
Query::TYPE_NOTEQUAL,
|
||||
Query::TYPE_LESSER,
|
||||
Query::TYPE_LESSEREQUAL,
|
||||
Query::TYPE_GREATER,
|
||||
Query::TYPE_GREATEREQUAL,
|
||||
Query::TYPE_SEARCH,
|
||||
Query::TYPE_IS_NULL,
|
||||
Query::TYPE_IS_NOT_NULL,
|
||||
Query::TYPE_BETWEEN,
|
||||
Query::TYPE_STARTS_WITH,
|
||||
Query::TYPE_ENDS_WITH => Base::METHOD_TYPE_FILTER,
|
||||
default => '',
|
||||
};
|
||||
|
||||
$methodIsValid = false;
|
||||
foreach ($this->validators as $validator) {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use Appwrite\Utopia\Database\Validator\Query\Offset;
|
|||
use Appwrite\Utopia\Database\Validator\Query\Cursor;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Select;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
|
@ -19,6 +20,7 @@ class Base extends Queries
|
|||
*
|
||||
* @param string $collection
|
||||
* @param string[] $allowedAttributes
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(string $collection, array $allowedAttributes)
|
||||
{
|
||||
|
|
@ -65,6 +67,7 @@ class Base extends Queries
|
|||
new Cursor(),
|
||||
new Filter($attributes),
|
||||
new Order($attributes),
|
||||
new Select($attributes),
|
||||
];
|
||||
|
||||
parent::__construct(...$validators);
|
||||
|
|
|
|||
46
src/Appwrite/Utopia/Database/Validator/Queries/Document.php
Normal file
46
src/Appwrite/Utopia/Database/Validator/Queries/Document.php
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Queries;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Cursor;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Select;
|
||||
use Utopia\Database\Database;
|
||||
|
||||
class Document extends Queries
|
||||
{
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
* @param array $attributes
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(array $attributes)
|
||||
{
|
||||
$attributes[] = new \Utopia\Database\Document([
|
||||
'key' => '$id',
|
||||
'type' => Database::VAR_STRING,
|
||||
'array' => false,
|
||||
]);
|
||||
$attributes[] = new \Utopia\Database\Document([
|
||||
'key' => '$createdAt',
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'array' => false,
|
||||
]);
|
||||
$attributes[] = new \Utopia\Database\Document([
|
||||
'key' => '$updatedAt',
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'array' => false,
|
||||
]);
|
||||
|
||||
$validators = [
|
||||
new Select($attributes),
|
||||
];
|
||||
|
||||
parent::__construct(...$validators);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
namespace Appwrite\Utopia\Database\Validator\Queries;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Queries;
|
||||
use Appwrite\Utopia\Database\Validator\IndexedQueries;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Cursor;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Filter;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Limit;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Offset;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Order;
|
||||
use Appwrite\Utopia\Database\Validator\Query\Select;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class Documents extends Queries
|
||||
class Documents extends IndexedQueries
|
||||
{
|
||||
/**
|
||||
* Expression constructor
|
||||
|
|
@ -19,7 +20,7 @@ class Documents extends Queries
|
|||
* @param Document[] $attributes
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(array $attributes)
|
||||
public function __construct(array $attributes, array $indexes)
|
||||
{
|
||||
$attributes[] = new Document([
|
||||
'key' => '$id',
|
||||
|
|
@ -43,8 +44,9 @@ class Documents extends Queries
|
|||
new Cursor(),
|
||||
new Filter($attributes),
|
||||
new Order($attributes),
|
||||
new Select($attributes),
|
||||
];
|
||||
|
||||
parent::__construct(...$validators);
|
||||
parent::__construct($attributes, $indexes, ...$validators);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ abstract class Base extends Validator
|
|||
public const METHOD_TYPE_CURSOR = 'cursor';
|
||||
public const METHOD_TYPE_ORDER = 'order';
|
||||
public const METHOD_TYPE_FILTER = 'filter';
|
||||
public const METHOD_TYPE_SELECT = 'select';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ class Filter extends Base
|
|||
*/
|
||||
protected $schema = [];
|
||||
|
||||
private int $maxValuesCount;
|
||||
|
||||
/**
|
||||
* Query constructor
|
||||
*
|
||||
|
|
@ -34,6 +36,18 @@ class Filter extends Base
|
|||
|
||||
protected function isValidAttribute($attribute): bool
|
||||
{
|
||||
if (\str_contains($attribute, '.')) {
|
||||
// For relationships, just validate the top level.
|
||||
// Utopia will validate each nested level during the recursive calls.
|
||||
$attribute = \explode('.', $attribute)[0];
|
||||
|
||||
// TODO: Remove this when nested queries are supported
|
||||
if (isset($this->schema[$attribute])) {
|
||||
$this->message = 'Cannot query nested attribute on: ' . $attribute;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Search for attribute in schema
|
||||
if (!isset($this->schema[$attribute])) {
|
||||
$this->message = 'Attribute not found in schema: ' . $attribute;
|
||||
|
|
@ -49,6 +63,12 @@ class Filter extends Base
|
|||
return false;
|
||||
}
|
||||
|
||||
if (\str_contains($attribute, '.')) {
|
||||
// For relationships, just validate the top level.
|
||||
// Utopia will validate each nested level during the recursive calls.
|
||||
$attribute = \explode('.', $attribute)[0];
|
||||
}
|
||||
|
||||
$attributeSchema = $this->schema[$attribute];
|
||||
|
||||
if (count($values) > $this->maxValuesCount) {
|
||||
|
|
@ -61,7 +81,9 @@ class Filter extends Base
|
|||
|
||||
foreach ($values as $value) {
|
||||
$condition = match ($attributeType) {
|
||||
Database::VAR_RELATIONSHIP => true,
|
||||
Database::VAR_DATETIME => gettype($value) === Database::VAR_STRING,
|
||||
Database::VAR_FLOAT => (gettype($value) === Database::VAR_FLOAT || gettype($value) === Database::VAR_INTEGER),
|
||||
default => gettype($value) === $attributeType
|
||||
};
|
||||
|
||||
|
|
@ -99,6 +121,11 @@ class Filter extends Base
|
|||
case Query::TYPE_GREATER:
|
||||
case Query::TYPE_GREATEREQUAL:
|
||||
case Query::TYPE_SEARCH:
|
||||
case Query::TYPE_STARTS_WITH:
|
||||
case Query::TYPE_ENDS_WITH:
|
||||
case Query::TYPE_BETWEEN:
|
||||
case Query::TYPE_IS_NULL:
|
||||
case Query::TYPE_IS_NOT_NULL:
|
||||
$values = $query->getValues();
|
||||
return $this->isValidAttributeAndValues($attribute, $values);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class Limit extends Base
|
|||
*
|
||||
* @param int $maxLimit
|
||||
*/
|
||||
public function __construct(int $maxLimit = 100)
|
||||
public function __construct(int $maxLimit = PHP_INT_MAX)
|
||||
{
|
||||
$this->maxLimit = $maxLimit;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Appwrite\Utopia\Database\Validator\Query\Base;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Validator\Range;
|
||||
|
||||
|
|
@ -15,7 +14,7 @@ class Offset extends Base
|
|||
*
|
||||
* @param int $maxOffset
|
||||
*/
|
||||
public function __construct(int $maxOffset = 5000)
|
||||
public function __construct(int $maxOffset = PHP_INT_MAX)
|
||||
{
|
||||
$this->maxOffset = $maxOffset;
|
||||
}
|
||||
|
|
|
|||
60
src/Appwrite/Utopia/Database/Validator/Query/Select.php
Normal file
60
src/Appwrite/Utopia/Database/Validator/Query/Select.php
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Database\Validator\Query;
|
||||
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
class Select extends Base
|
||||
{
|
||||
protected array $schema = [];
|
||||
|
||||
/**
|
||||
* Query constructor
|
||||
*
|
||||
*/
|
||||
public function __construct(array $attributes = [])
|
||||
{
|
||||
foreach ($attributes as $attribute) {
|
||||
$this->schema[$attribute->getAttribute('key')] = $attribute->getArrayCopy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Is valid.
|
||||
*
|
||||
* Returns true if method is TYPE_SELECT selections are valid
|
||||
*
|
||||
* Otherwise, returns false
|
||||
*
|
||||
* @param $query
|
||||
* @return bool
|
||||
*/
|
||||
public function isValid($query): bool
|
||||
{
|
||||
/* @var $query Query */
|
||||
|
||||
if ($query->getMethod() !== Query::TYPE_SELECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($query->getValues() as $attribute) {
|
||||
if (\str_contains($attribute, '.')) {
|
||||
// For relationships, just validate the top level.
|
||||
// Utopia will validate each nested level during the recursive calls.
|
||||
$attribute = \explode('.', $attribute)[0];
|
||||
}
|
||||
if (!isset($this->schema[$attribute]) && $attribute !== '*') {
|
||||
$this->message = 'Attribute not found in schema: ' . $attribute;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getMethodType(): string
|
||||
{
|
||||
return self::METHOD_TYPE_SELECT;
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ use Appwrite\Utopia\Response\Model\AttributeEnum;
|
|||
use Appwrite\Utopia\Response\Model\AttributeIP;
|
||||
use Appwrite\Utopia\Response\Model\AttributeURL;
|
||||
use Appwrite\Utopia\Response\Model\AttributeDatetime;
|
||||
use Appwrite\Utopia\Response\Model\AttributeRelationship;
|
||||
use Appwrite\Utopia\Response\Model\BaseList;
|
||||
use Appwrite\Utopia\Response\Model\Collection;
|
||||
use Appwrite\Utopia\Response\Model\Database;
|
||||
|
|
@ -132,6 +133,7 @@ class Response extends SwooleResponse
|
|||
public const MODEL_ATTRIBUTE_IP = 'attributeIp';
|
||||
public const MODEL_ATTRIBUTE_URL = 'attributeUrl';
|
||||
public const MODEL_ATTRIBUTE_DATETIME = 'attributeDatetime';
|
||||
public const MODEL_ATTRIBUTE_RELATIONSHIP = 'attributeRelationship';
|
||||
|
||||
// Users
|
||||
public const MODEL_ACCOUNT = 'account';
|
||||
|
|
@ -288,6 +290,7 @@ class Response extends SwooleResponse
|
|||
->setModel(new AttributeIP())
|
||||
->setModel(new AttributeURL())
|
||||
->setModel(new AttributeDatetime())
|
||||
->setModel(new AttributeRelationship())
|
||||
->setModel(new Index())
|
||||
->setModel(new ModelDocument())
|
||||
->setModel(new Log())
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ abstract class Model
|
|||
public const TYPE_JSON = 'json';
|
||||
public const TYPE_DATETIME = 'datetime';
|
||||
public const TYPE_DATETIME_EXAMPLE = '2020-10-15T06:38:00.000+00:00';
|
||||
public const TYPE_RELATIONSHIP = 'relationship';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
|
|
@ -80,6 +81,7 @@ abstract class Model
|
|||
*
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return Model
|
||||
*/
|
||||
protected function addRule(string $key, array $options): self
|
||||
{
|
||||
|
|
@ -98,7 +100,7 @@ abstract class Model
|
|||
* If rule exists, it will be removed
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $options
|
||||
* @return Model
|
||||
*/
|
||||
protected function removeRule(string $key): self
|
||||
{
|
||||
|
|
@ -109,7 +111,10 @@ abstract class Model
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getRequired()
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getRequired(): array
|
||||
{
|
||||
$list = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeBoolean extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeEmail extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeEnum extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeFloat extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeIP extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeInteger extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ namespace Appwrite\Utopia\Response\Model;
|
|||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
class AttributeList extends Model
|
||||
{
|
||||
|
|
@ -27,6 +26,7 @@ class AttributeList extends Model
|
|||
Response::MODEL_ATTRIBUTE_URL,
|
||||
Response::MODEL_ATTRIBUTE_IP,
|
||||
Response::MODEL_ATTRIBUTE_DATETIME,
|
||||
Response::MODEL_ATTRIBUTE_RELATIONSHIP,
|
||||
Response::MODEL_ATTRIBUTE_STRING // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'List of attributes.',
|
||||
|
|
|
|||
76
src/Appwrite/Utopia/Response/Model/AttributeRelationship.php
Normal file
76
src/Appwrite/Utopia/Response/Model/AttributeRelationship.php
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
|
||||
class AttributeRelationship extends Attribute
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this
|
||||
->addRule('relatedCollection', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The ID of the related collection.',
|
||||
'default' => null,
|
||||
'example' => 'collection',
|
||||
])
|
||||
->addRule('relationType', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The type of the relationship.',
|
||||
'default' => '',
|
||||
'example' => 'oneToOne|oneToMany|manyToOne|manyToMany',
|
||||
])
|
||||
->addRule('twoWay', [
|
||||
'type' => self::TYPE_BOOLEAN,
|
||||
'description' => 'Is the relationship two-way?',
|
||||
'default' => false,
|
||||
'example' => false,
|
||||
])
|
||||
->addRule('twoWayKey', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'The key of the two-way relationship.',
|
||||
'default' => '',
|
||||
'example' => 'string',
|
||||
])
|
||||
->addRule('onDelete', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'How deleting the parent document will propagate to child documents.',
|
||||
'default' => 'restrict',
|
||||
'example' => 'restrict|cascade|setNull',
|
||||
])
|
||||
->addRule('side', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Whether this is the parent or child side of the relationship',
|
||||
'default' => '',
|
||||
'example' => 'parent|child',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public array $conditions = [
|
||||
'type' => self::TYPE_RELATIONSHIP,
|
||||
];
|
||||
|
||||
/**
|
||||
* Get Name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return 'AttributeRelationship';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getType(): string
|
||||
{
|
||||
return Response::MODEL_ATTRIBUTE_RELATIONSHIP;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeString extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
namespace Appwrite\Utopia\Response\Model;
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Model\Attribute;
|
||||
|
||||
class AttributeURL extends Attribute
|
||||
{
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ class Collection extends Model
|
|||
Response::MODEL_ATTRIBUTE_URL,
|
||||
Response::MODEL_ATTRIBUTE_IP,
|
||||
Response::MODEL_ATTRIBUTE_DATETIME,
|
||||
Response::MODEL_ATTRIBUTE_RELATIONSHIP,
|
||||
Response::MODEL_ATTRIBUTE_STRING, // needs to be last, since its condition would dominate any other string attribute
|
||||
],
|
||||
'description' => 'Collection attributes.',
|
||||
|
|
|
|||
|
|
@ -74,6 +74,18 @@ class Document extends Any
|
|||
$document->removeAttribute('$internalId');
|
||||
$document->removeAttribute('$collection'); // $collection is the internal collection ID
|
||||
|
||||
foreach ($document->getAttributes() as $attribute) {
|
||||
if (\is_array($attribute)) {
|
||||
foreach ($attribute as $subAttribute) {
|
||||
if ($subAttribute instanceof DatabaseDocument) {
|
||||
$this->filter($subAttribute);
|
||||
}
|
||||
}
|
||||
} elseif ($attribute instanceof DatabaseDocument) {
|
||||
$this->filter($attribute);
|
||||
}
|
||||
}
|
||||
|
||||
return $document;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,12 @@ class Team extends Model
|
|||
'default' => 0,
|
||||
'example' => 7,
|
||||
])
|
||||
->addRule('prefs', [
|
||||
'type' => Response::MODEL_PREFERENCES,
|
||||
'description' => 'Team preferences as a key-value object',
|
||||
'default' => new \stdClass(),
|
||||
'example' => ['theme' => 'pink', 'timezone' => 'UTC'],
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,12 +38,14 @@ class User extends Model
|
|||
->addRule('password', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Hashed user password.',
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'example' => '$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L/4LdgrVRXxE',
|
||||
])
|
||||
->addRule('hash', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Password hashing algorithm.',
|
||||
'required' => false,
|
||||
'default' => '',
|
||||
'example' => 'argon2',
|
||||
])
|
||||
|
|
@ -58,6 +60,7 @@ class User extends Model
|
|||
Response::MODEL_ALGO_MD5, // keep least secure at the bottom. this order will be used in docs
|
||||
],
|
||||
'description' => 'Password hashing algorithm configuration.',
|
||||
'required' => false,
|
||||
'default' => [],
|
||||
'example' => new \stdClass(),
|
||||
'array' => false,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use CURLFile;
|
|||
use Tests\E2E\Services\Functions\FunctionsBase;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
class UsageTest extends Scope
|
||||
{
|
||||
|
|
|
|||
|
|
@ -46,14 +46,7 @@ trait ProjectCustom
|
|||
'name' => 'Demo Project',
|
||||
'teamId' => $team['body']['$id'],
|
||||
'description' => 'Demo Project Description',
|
||||
'logo' => '',
|
||||
'url' => 'https://appwrite.io',
|
||||
'legalName' => '',
|
||||
'legalCountry' => '',
|
||||
'legalState' => '',
|
||||
'legalCity' => '',
|
||||
'legalAddress' => '',
|
||||
'legalTaxId' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $project['headers']['status-code']);
|
||||
|
|
@ -111,8 +104,6 @@ trait ProjectCustom
|
|||
],
|
||||
'url' => 'http://request-catcher:5000/webhook',
|
||||
'security' => false,
|
||||
'httpUser' => '',
|
||||
'httpPass' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $webhook['headers']['status-code']);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use Appwrite\Tests\Retry;
|
|||
use Tests\E2E\Client;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
trait AccountBase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use Tests\E2E\Scopes\ProjectCustom;
|
|||
use Tests\E2E\Scopes\SideClient;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
use function sleep;
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -53,7 +53,7 @@ class DatabasesCustomClientTest extends Scope
|
|||
$this->assertContains(Permission::update(Role::user($this->getUser()['$id'])), $movies['body']['$permissions']);
|
||||
$this->assertContains(Permission::delete(Role::user($this->getUser()['$id'])), $movies['body']['$permissions']);
|
||||
|
||||
$this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/attributes/string', array_merge([
|
||||
$response = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/attributes/string', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
|
|
@ -65,6 +65,8 @@ class DatabasesCustomClientTest extends Scope
|
|||
|
||||
sleep(1);
|
||||
|
||||
$this->assertEquals(202, $response['headers']['status-code']);
|
||||
|
||||
// Document aliases write to update, delete
|
||||
$document1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $moviesId . '/documents', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -176,7 +176,7 @@ class DatabasesPermissionsTeamTest extends Scope
|
|||
if ($success) {
|
||||
$this->assertCount(1, $documents['body']['documents']);
|
||||
} else {
|
||||
$this->assertEquals(401, $documents['headers']['status-code']);
|
||||
$this->assertCount(0, $documents['body']['documents']);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
], [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
@ -223,6 +224,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
], [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
@ -321,6 +323,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
], [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
@ -549,6 +552,7 @@ class FunctionsCustomClientTest extends Scope
|
|||
], [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)), //different tarball names intentional
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Tests\E2E\Scopes\ProjectCustom;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
class FunctionsCustomServerTest extends Scope
|
||||
{
|
||||
|
|
@ -363,6 +363,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
@ -411,6 +412,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$largeTag = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/deployments', array_merge($headers, $this->getHeaders()), [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => $curlFile,
|
||||
'activate' => true
|
||||
]);
|
||||
$counter++;
|
||||
$id = $largeTag['body']['$id'];
|
||||
|
|
@ -741,7 +743,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $data['functionId'] . '/executions', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
|
@ -752,7 +753,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
|
||||
$this->assertEquals('completed', $execution['body']['status']);
|
||||
$this->assertStringContainsString($data['deploymentId'], $execution['body']['response']);
|
||||
$this->assertStringContainsString('Test1', $execution['body']['response']);
|
||||
$this->assertStringContainsString('http', $execution['body']['response']);
|
||||
$this->assertStringContainsString('PHP', $execution['body']['response']);
|
||||
|
|
@ -869,7 +869,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
|
|
@ -953,7 +952,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
|
|
@ -967,6 +965,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'entrypoint' => $entrypoint,
|
||||
'code' => new CURLFile($code, 'application/x-gzip', basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
@ -1063,7 +1062,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
|
|
@ -1176,7 +1174,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
|
|
@ -1290,7 +1287,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
|
|
@ -1404,7 +1400,6 @@ class FunctionsCustomServerTest extends Scope
|
|||
'name' => 'Test ' . $name,
|
||||
'runtime' => $name,
|
||||
'events' => [],
|
||||
'schedule' => '',
|
||||
'timeout' => $timeout,
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,19 @@ trait Base
|
|||
public static string $CREATE_IP_ATTRIBUTE = 'create_ip_attribute';
|
||||
public static string $CREATE_ENUM_ATTRIBUTE = 'create_enum_attribute';
|
||||
public static string $CREATE_DATETIME_ATTRIBUTE = 'create_datetime_attribute';
|
||||
|
||||
public static string $CREATE_RELATIONSHIP_ATTRIBUTE = 'create_relationship_attribute';
|
||||
public static string $UPDATE_STRING_ATTRIBUTE = 'update_string_attribute';
|
||||
public static string $UPDATE_INTEGER_ATTRIBUTE = 'update_integer_attribute';
|
||||
public static string $UPDATE_FLOAT_ATTRIBUTE = 'update_float_attribute';
|
||||
public static string $UPDATE_BOOLEAN_ATTRIBUTE = 'update_boolean_attribute';
|
||||
public static string $UPDATE_URL_ATTRIBUTE = 'update_url_attribute';
|
||||
public static string $UPDATE_EMAIL_ATTRIBUTE = 'update_email_attribute';
|
||||
public static string $UPDATE_IP_ATTRIBUTE = 'update_ip_attribute';
|
||||
public static string $UPDATE_ENUM_ATTRIBUTE = 'update_enum_attribute';
|
||||
public static string $UPDATE_DATETIME_ATTRIBUTE = 'update_datetime_attribute';
|
||||
|
||||
public static string $UPDATE_RELATIONSHIP_ATTRIBUTE = 'update_relationship_attribute';
|
||||
public static string $GET_ATTRIBUTES = 'get_attributes';
|
||||
public static string $GET_ATTRIBUTE = 'get_attribute';
|
||||
public static string $DELETE_ATTRIBUTE = 'delete_attribute';
|
||||
|
|
@ -97,9 +110,12 @@ trait Base
|
|||
|
||||
// Teams
|
||||
public static string $GET_TEAM = 'get_team';
|
||||
public static string $GET_TEAM_PREFERENCES = 'get_team_preferences';
|
||||
public static string $GET_TEAMS = 'list_teams';
|
||||
public static string $CREATE_TEAM = 'create_team';
|
||||
public static string $UPDATE_TEAM = 'update_team';
|
||||
public static string $UPDATE_TEAM_NAME = 'update_team_name';
|
||||
public static string $UPDATE_TEAM_PREFERENCES = 'update_team_preferences';
|
||||
|
||||
public static string $DELETE_TEAM = 'delete_team';
|
||||
public static string $GET_TEAM_MEMBERSHIP = 'get_team_membership';
|
||||
public static string $GET_TEAM_MEMBERSHIPS = 'list_team_memberships';
|
||||
|
|
@ -451,6 +467,96 @@ trait Base
|
|||
array
|
||||
}
|
||||
}';
|
||||
case self::$CREATE_RELATIONSHIP_ATTRIBUTE:
|
||||
return 'mutation createRelationshipAttribute($databaseId: String!, $collectionId: String!, $relatedCollectionId: String!, $type: String!, $twoWay: Boolean, $key: String, $twoWayKey: String, $onDelete: String){
|
||||
databasesCreateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, relatedCollectionId: $relatedCollectionId, type: $type, twoWay: $twoWay, key: $key, twoWayKey: $twoWayKey, onDelete: $onDelete) {
|
||||
relatedCollection
|
||||
relationType
|
||||
twoWay
|
||||
key
|
||||
twoWayKey
|
||||
onDelete
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_STRING_ATTRIBUTE:
|
||||
return 'mutation updateStringAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){
|
||||
databasesUpdateStringAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) {
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_INTEGER_ATTRIBUTE:
|
||||
return 'mutation updateIntegerAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Int!, $max: Int!, $default: Int){
|
||||
databasesUpdateIntegerAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, min: $min, max: $max, default: $default) {
|
||||
required
|
||||
min
|
||||
max
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_FLOAT_ATTRIBUTE:
|
||||
return 'mutation updateFloatAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $min: Float!, $max: Float!, $default: Float){
|
||||
databasesUpdateFloatAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, min: $min, max: $max, required: $required, default: $default) {
|
||||
required
|
||||
min
|
||||
max
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_BOOLEAN_ATTRIBUTE:
|
||||
return 'mutation updateBooleanAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: Boolean){
|
||||
databasesUpdateBooleanAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) {
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_URL_ATTRIBUTE:
|
||||
return 'mutation updateUrlAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){
|
||||
databasesUpdateUrlAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) {
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_EMAIL_ATTRIBUTE:
|
||||
return 'mutation updateEmailAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){
|
||||
databasesUpdateEmailAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) {
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_IP_ATTRIBUTE:
|
||||
return 'mutation updateIpAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){
|
||||
databasesUpdateIpAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) {
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_ENUM_ATTRIBUTE:
|
||||
return 'mutation updateEnumAttribute($databaseId: String!, $collectionId: String!, $key: String!, $elements: [String!]!, $required: Boolean!, $default: String){
|
||||
databasesUpdateEnumAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, elements: $elements, required: $required, default: $default) {
|
||||
elements
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_DATETIME_ATTRIBUTE:
|
||||
return 'mutation updateDatetimeAttribute($databaseId: String!, $collectionId: String!, $key: String!, $required: Boolean!, $default: String){
|
||||
databasesUpdateDatetimeAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, required: $required, default: $default) {
|
||||
required
|
||||
default
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_RELATIONSHIP_ATTRIBUTE:
|
||||
return 'mutation updateRelationshipAttribute($databaseId: String!, $collectionId: String!, $key: String!, $onDelete: String){
|
||||
databasesUpdateRelationshipAttribute(databaseId: $databaseId, collectionId: $collectionId, key: $key, onDelete: $onDelete) {
|
||||
relatedCollection
|
||||
relationType
|
||||
twoWay
|
||||
key
|
||||
twoWayKey
|
||||
onDelete
|
||||
}
|
||||
}';
|
||||
case self::$CREATE_INDEX:
|
||||
return 'mutation createIndex($databaseId: String!, $collectionId: String!, $key: String!, $type: String!, $attributes: [String!]!, $orders: [String!]){
|
||||
databasesCreateIndex(databaseId: $databaseId, collectionId: $collectionId, key: $key, type: $type, attributes: $attributes, orders: $orders) {
|
||||
|
|
@ -1103,10 +1209,16 @@ trait Base
|
|||
}';
|
||||
case self::$GET_TEAM:
|
||||
return 'query getTeam($teamId: String!){
|
||||
teamsGet(teamId: $teamId) {
|
||||
_id
|
||||
name
|
||||
total
|
||||
teamsGet(teamId: $teamId) {
|
||||
_id
|
||||
name
|
||||
total
|
||||
}
|
||||
}';
|
||||
case self::$GET_TEAM_PREFERENCES:
|
||||
return 'query getTeamPreferences($teamId: String!) {
|
||||
teamsGetPrefs(teamId: $teamId) {
|
||||
data
|
||||
}
|
||||
}';
|
||||
case self::$GET_TEAMS:
|
||||
|
|
@ -1127,12 +1239,18 @@ trait Base
|
|||
total
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_TEAM:
|
||||
return 'mutation updateTeam($teamId: String!, $name: String!){
|
||||
teamsUpdate(teamId: $teamId, name : $name) {
|
||||
_id
|
||||
name
|
||||
total
|
||||
case self::$UPDATE_TEAM_NAME:
|
||||
return 'mutation updateTeamName($teamId: String!, $name: String!){
|
||||
teamsUpdateName(teamId: $teamId, name : $name) {
|
||||
_id
|
||||
name
|
||||
total
|
||||
}
|
||||
}';
|
||||
case self::$UPDATE_TEAM_PREFERENCES:
|
||||
return 'mutation updateTeamPrefs($teamId: String!, $prefs: Assoc!){
|
||||
teamsUpdatePrefs(teamId: $teamId, prefs: $prefs) {
|
||||
data
|
||||
}
|
||||
}';
|
||||
case self::$DELETE_TEAM:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use Tests\E2E\Client;
|
|||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
|
@ -75,9 +76,36 @@ class DatabaseServerTest extends Scope
|
|||
$collection = $collection['body']['data']['databasesCreateCollection'];
|
||||
$this->assertEquals('Actors', $collection['name']);
|
||||
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $database['_id'],
|
||||
'collectionId' => 'movies',
|
||||
'name' => 'Movies',
|
||||
'documentSecurity' => false,
|
||||
'permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::create(Role::users()),
|
||||
Permission::update(Role::users()),
|
||||
Permission::delete(Role::users()),
|
||||
],
|
||||
]
|
||||
];
|
||||
|
||||
$collection2 = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($collection2['body']['data']);
|
||||
$this->assertArrayNotHasKey('errors', $collection2['body']);
|
||||
$collection2 = $collection2['body']['data']['databasesCreateCollection'];
|
||||
$this->assertEquals('Movies', $collection2['name']);
|
||||
|
||||
return [
|
||||
'database' => $database,
|
||||
'collection' => $collection,
|
||||
'collection2' => $collection2,
|
||||
];
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +140,42 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateStringAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateStringAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_STRING_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'name',
|
||||
'required' => false,
|
||||
'default' => 'Default Value',
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateStringAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateStringAttribute']['required']);
|
||||
$this->assertEquals('Default Value', $attribute['body']['data']['databasesUpdateStringAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -144,6 +208,46 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateIntegerAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateIntegerAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_INTEGER_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'age',
|
||||
'required' => false,
|
||||
'min' => 12,
|
||||
'max' => 160,
|
||||
'default' => 50
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateIntegerAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateIntegerAttribute']['required']);
|
||||
$this->assertEquals(12, $attribute['body']['data']['databasesUpdateIntegerAttribute']['min']);
|
||||
$this->assertEquals(160, $attribute['body']['data']['databasesUpdateIntegerAttribute']['max']);
|
||||
$this->assertEquals(50, $attribute['body']['data']['databasesUpdateIntegerAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -174,6 +278,42 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateBooleanAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateBooleanAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_BOOLEAN_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'alive',
|
||||
'required' => false,
|
||||
'default' => true
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateBooleanAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateBooleanAttribute']['required']);
|
||||
$this->assertTrue($attribute['body']['data']['databasesUpdateBooleanAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -207,6 +347,46 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateFloatAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateFloatAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_FLOAT_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'salary',
|
||||
'required' => false,
|
||||
'min' => 100.0,
|
||||
'max' => 1000000.0,
|
||||
'default' => 2500.0
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateFloatAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateFloatAttribute']['required']);
|
||||
$this->assertEquals(100.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['min']);
|
||||
$this->assertEquals(1000000.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['max']);
|
||||
$this->assertEquals(2500.0, $attribute['body']['data']['databasesUpdateFloatAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -237,6 +417,42 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateEmailAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateEmailAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_EMAIL_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'email',
|
||||
'required' => false,
|
||||
'default' => 'torsten@appwrite.io',
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateEmailAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateEmailAttribute']['required']);
|
||||
$this->assertEquals('torsten@appwrite.io', $attribute['body']['data']['databasesUpdateEmailAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -272,6 +488,50 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @depends testCreateEnumAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateEnumAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_ENUM_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'role',
|
||||
'required' => false,
|
||||
'elements' => [
|
||||
'crew',
|
||||
'tech',
|
||||
'actor'
|
||||
],
|
||||
'default' => 'tech'
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateEnumAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateEnumAttribute']['required']);
|
||||
$this->assertEquals('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['default']);
|
||||
$this->assertContains('tech', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']);
|
||||
$this->assertNotContains('guest', $attribute['body']['data']['databasesUpdateEnumAttribute']['elements']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -302,6 +562,105 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateDatetimeAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateDatetimeAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_DATETIME_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'dob',
|
||||
'required' => false,
|
||||
'default' => '2000-01-01T00:00:00Z'
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateDatetimeAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateDatetimeAttribute']['required']);
|
||||
$this->assertEquals('2000-01-01T00:00:00Z', $attribute['body']['data']['databasesUpdateDatetimeAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
*/
|
||||
public function testCreateRelationshipAttribute(array $data): array
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$CREATE_RELATIONSHIP_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection2']['_id'], // Movies
|
||||
'relatedCollectionId' => $data['collection']['_id'], // Actors
|
||||
'type' => Database::RELATION_ONE_TO_MANY,
|
||||
'twoWay' => true,
|
||||
'key' => 'actors',
|
||||
'twoWayKey' => 'movie'
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertArrayNotHasKey('errors', $attribute['body']);
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesCreateRelationshipAttribute']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateRelationshipAttribute
|
||||
*/
|
||||
public function testUpdateRelationshipAttribute(array $data): array
|
||||
{
|
||||
sleep(1);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_RELATIONSHIP_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection2']['_id'],
|
||||
'key' => 'actors',
|
||||
'onDelete' => Database::RELATION_MUTATE_CASCADE,
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertArrayNotHasKey('errors', $attribute['body']);
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateRelationshipAttribute']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -333,6 +692,42 @@ class DatabaseServerTest extends Scope
|
|||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateIPAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testUpdateIPAttribute($data): array
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(3);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_IP_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'ip',
|
||||
'required' => false,
|
||||
'default' => '127.0.0.1'
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateIpAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateIpAttribute']['required']);
|
||||
$this->assertEquals('127.0.0.1', $attribute['body']['data']['databasesUpdateIpAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateCollection
|
||||
* @throws Exception
|
||||
|
|
@ -365,15 +760,46 @@ class DatabaseServerTest extends Scope
|
|||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateStringAttribute
|
||||
* @depends testCreateIntegerAttribute
|
||||
* @depends testCreateURLAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testCreateIndex($data): array
|
||||
public function testUpdateURLAttribute($data): void
|
||||
{
|
||||
// Wait for attributes to be available
|
||||
sleep(3);
|
||||
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_URL_ATTRIBUTE);
|
||||
$gqlPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'databaseId' => $data['database']['_id'],
|
||||
'collectionId' => $data['collection']['_id'],
|
||||
'key' => 'url',
|
||||
'required' => false,
|
||||
'default' => 'https://cloud.appwrite.io'
|
||||
]
|
||||
];
|
||||
|
||||
$attribute = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($attribute['body']['data']);
|
||||
$this->assertIsArray($attribute['body']['data']['databasesUpdateUrlAttribute']);
|
||||
$this->assertFalse($attribute['body']['data']['databasesUpdateUrlAttribute']['required']);
|
||||
$this->assertEquals('https://cloud.appwrite.io', $attribute['body']['data']['databasesUpdateUrlAttribute']['default']);
|
||||
$this->assertEquals(200, $attribute['headers']['status-code']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateStringAttribute
|
||||
* @depends testUpdateIntegerAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testCreateIndex($data): array
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$CREATE_INDEX);
|
||||
$gqlPayload = [
|
||||
|
|
@ -407,10 +833,10 @@ class DatabaseServerTest extends Scope
|
|||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateStringAttribute
|
||||
* @depends testCreateIntegerAttribute
|
||||
* @depends testCreateBooleanAttribute
|
||||
* @depends testCreateEnumAttribute
|
||||
* @depends testUpdateStringAttribute
|
||||
* @depends testUpdateIntegerAttribute
|
||||
* @depends testUpdateBooleanAttribute
|
||||
* @depends testUpdateEnumAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testCreateDocument($data): array
|
||||
|
|
@ -458,45 +884,45 @@ class DatabaseServerTest extends Scope
|
|||
];
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @depends testCreateStringAttribute
|
||||
// * @depends testCreateIntegerAttribute
|
||||
// * @depends testCreateBooleanAttribute
|
||||
// * @depends testCreateFloatAttribute
|
||||
// * @depends testCreateEmailAttribute
|
||||
// * @depends testCreateEnumAttribute
|
||||
// * @depends testCreateDatetimeAttribute
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testCreateCustomEntity(): array
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$CREATE_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'name' => 'John Doe',
|
||||
// 'age' => 35,
|
||||
// 'alive' => true,
|
||||
// 'salary' => 9999.9,
|
||||
// 'email' => 'johndoe@appwrite.io',
|
||||
// 'role' => 'crew',
|
||||
// 'dob' => '2000-01-01T00:00:00Z',
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $actor = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $actor['body']);
|
||||
// $this->assertIsArray($actor['body']['data']);
|
||||
// $actor = $actor['body']['data']['actorsCreate'];
|
||||
// $this->assertIsArray($actor);
|
||||
//
|
||||
// return $actor;
|
||||
// }
|
||||
// /**
|
||||
// * @depends testCreateStringAttribute
|
||||
// * @depends testCreateIntegerAttribute
|
||||
// * @depends testCreateBooleanAttribute
|
||||
// * @depends testCreateFloatAttribute
|
||||
// * @depends testCreateEmailAttribute
|
||||
// * @depends testCreateEnumAttribute
|
||||
// * @depends testCreateDatetimeAttribute
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testCreateCustomEntity(): array
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$CREATE_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'name' => 'John Doe',
|
||||
// 'age' => 35,
|
||||
// 'alive' => true,
|
||||
// 'salary' => 9999.9,
|
||||
// 'email' => 'johndoe@appwrite.io',
|
||||
// 'role' => 'crew',
|
||||
// 'dob' => '2000-01-01T00:00:00Z',
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $actor = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $actor['body']);
|
||||
// $this->assertIsArray($actor['body']['data']);
|
||||
// $actor = $actor['body']['data']['actorsCreate'];
|
||||
// $this->assertIsArray($actor);
|
||||
//
|
||||
// return $actor;
|
||||
// }
|
||||
|
||||
public function testGetDatabases(): void
|
||||
{
|
||||
|
|
@ -593,8 +1019,8 @@ class DatabaseServerTest extends Scope
|
|||
}
|
||||
|
||||
/**
|
||||
* @depends testCreateStringAttribute
|
||||
* @depends testCreateIntegerAttribute
|
||||
* @depends testUpdateStringAttribute
|
||||
* @depends testUpdateIntegerAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testGetAttributes($data): void
|
||||
|
|
@ -752,52 +1178,52 @@ class DatabaseServerTest extends Scope
|
|||
$this->assertIsArray($document['body']['data']['databasesGetDocument']);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testGetCustomEntities($data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$GET_CUSTOM_ENTITIES);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// ];
|
||||
//
|
||||
// $customEntities = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $customEntities['body']);
|
||||
// $this->assertIsArray($customEntities['body']['data']);
|
||||
// $this->assertIsArray($customEntities['body']['data']['actorsList']);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testGetCustomEntity($data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$GET_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'id' => $data['id'],
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $entity['body']);
|
||||
// $this->assertIsArray($entity['body']['data']);
|
||||
// $this->assertIsArray($entity['body']['data']['actorsGet']);
|
||||
// }
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testGetCustomEntities($data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$GET_CUSTOM_ENTITIES);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// ];
|
||||
//
|
||||
// $customEntities = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $customEntities['body']);
|
||||
// $this->assertIsArray($customEntities['body']['data']);
|
||||
// $this->assertIsArray($customEntities['body']['data']['actorsList']);
|
||||
// }
|
||||
//
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testGetCustomEntity($data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$GET_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'id' => $data['id'],
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $entity['body']);
|
||||
// $this->assertIsArray($entity['body']['data']);
|
||||
// $this->assertIsArray($entity['body']['data']['actorsGet']);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @depends testCreateDatabase
|
||||
|
|
@ -885,33 +1311,33 @@ class DatabaseServerTest extends Scope
|
|||
$this->assertStringContainsString('New Document Name', $document['data']);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testUpdateCustomEntity(array $data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$UPDATE_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'id' => $data['id'],
|
||||
// 'name' => 'New Custom Entity Name',
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $entity['body']);
|
||||
// $this->assertIsArray($entity['body']['data']);
|
||||
// $entity = $entity['body']['data']['actorsUpdate'];
|
||||
// $this->assertIsArray($entity);
|
||||
// $this->assertStringContainsString('New Custom Entity Name', $entity['name']);
|
||||
// }
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testUpdateCustomEntity(array $data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$UPDATE_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'id' => $data['id'],
|
||||
// 'name' => 'New Custom Entity Name',
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertArrayNotHasKey('errors', $entity['body']);
|
||||
// $this->assertIsArray($entity['body']['data']);
|
||||
// $entity = $entity['body']['data']['actorsUpdate'];
|
||||
// $this->assertIsArray($entity);
|
||||
// $this->assertStringContainsString('New Custom Entity Name', $entity['name']);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @depends testCreateDocument
|
||||
|
|
@ -939,32 +1365,32 @@ class DatabaseServerTest extends Scope
|
|||
$this->assertEquals(204, $document['headers']['status-code']);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testDeleteCustomEntity(array $data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$DELETE_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'id' => $data['id'],
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertIsNotArray($entity['body']);
|
||||
// $this->assertEquals(204, $entity['headers']['status-code']);
|
||||
// }
|
||||
// /**
|
||||
// * @depends testCreateCustomEntity
|
||||
// * @throws Exception
|
||||
// */
|
||||
// public function testDeleteCustomEntity(array $data)
|
||||
// {
|
||||
// $projectId = $this->getProject()['$id'];
|
||||
// $query = $this->getQuery(self::$DELETE_CUSTOM_ENTITY);
|
||||
// $gqlPayload = [
|
||||
// 'query' => $query,
|
||||
// 'variables' => [
|
||||
// 'id' => $data['id'],
|
||||
// ]
|
||||
// ];
|
||||
//
|
||||
// $entity = $this->client->call(Client::METHOD_POST, '/graphql', array_merge([
|
||||
// 'content-type' => 'application/json',
|
||||
// 'x-appwrite-project' => $projectId,
|
||||
// ], $this->getHeaders()), $gqlPayload);
|
||||
//
|
||||
// $this->assertIsNotArray($entity['body']);
|
||||
// $this->assertEquals(204, $entity['headers']['status-code']);
|
||||
// }
|
||||
|
||||
/**
|
||||
* @depends testCreateStringAttribute
|
||||
* @depends testUpdateStringAttribute
|
||||
* @throws Exception
|
||||
*/
|
||||
public function testDeleteAttribute($data): void
|
||||
|
|
|
|||
|
|
@ -111,6 +111,62 @@ class TeamsServerTest extends Scope
|
|||
$this->assertArrayNotHasKey('errors', $team['body']);
|
||||
$team = $team['body']['data']['teamsGet'];
|
||||
$this->assertIsArray($team);
|
||||
|
||||
return $team;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetTeam
|
||||
*/
|
||||
public function testUpdateTeamPrefs($team)
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_TEAM_PREFERENCES);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'teamId' => $team['_id'],
|
||||
'prefs' => [
|
||||
'key' => 'value'
|
||||
]
|
||||
],
|
||||
];
|
||||
|
||||
$prefs = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertIsArray($prefs['body']['data']);
|
||||
$this->assertArrayNotHasKey('errors', $prefs['body']);
|
||||
$this->assertIsArray($prefs['body']['data']['teamsUpdatePrefs']);
|
||||
$this->assertEquals('{"key":"value"}', $prefs['body']['data']['teamsUpdatePrefs']['data']);
|
||||
|
||||
return $team;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testUpdateTeamPrefs
|
||||
*/
|
||||
public function testGetTeamPreferences($team)
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$GET_TEAM_PREFERENCES);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
'teamId' => $team['_id'],
|
||||
]
|
||||
];
|
||||
|
||||
$prefs = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertIsArray($prefs['body']['data']);
|
||||
$this->assertArrayNotHasKey('errors', $prefs['body']);
|
||||
$this->assertIsArray($prefs['body']['data']['teamsGetPrefs']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -168,7 +224,7 @@ class TeamsServerTest extends Scope
|
|||
public function testUpdateTeam($team)
|
||||
{
|
||||
$projectId = $this->getProject()['$id'];
|
||||
$query = $this->getQuery(self::$UPDATE_TEAM);
|
||||
$query = $this->getQuery(self::$UPDATE_TEAM_NAME);
|
||||
$graphQLPayload = [
|
||||
'query' => $query,
|
||||
'variables' => [
|
||||
|
|
@ -184,7 +240,7 @@ class TeamsServerTest extends Scope
|
|||
|
||||
$this->assertIsArray($team['body']['data']);
|
||||
$this->assertArrayNotHasKey('errors', $team['body']);
|
||||
$team = $team['body']['data']['teamsUpdate'];
|
||||
$team = $team['body']['data']['teamsUpdateName'];
|
||||
$this->assertEquals('New Name', $team['name']);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1655,8 +1655,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'events' => ['users.*.delete', 'users.*.sessions.*.delete', 'buckets.*.files.*.create'],
|
||||
'url' => 'https://appwrite.io/new',
|
||||
'security' => false,
|
||||
'httpUser' => '',
|
||||
'httpPass' => ''
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -1703,8 +1701,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'events' => ['users.*.delete', 'users.*.sessions.*.delete', 'buckets.*.files.*.unknown'],
|
||||
'url' => 'https://appwrite.io/new',
|
||||
'security' => false,
|
||||
'httpUser' => '',
|
||||
'httpPass' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
|
@ -1717,8 +1713,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'events' => ['users.*.delete', 'users.*.sessions.*.delete', 'buckets.*.files.*.create'],
|
||||
'url' => 'appwrite.io/new',
|
||||
'security' => false,
|
||||
'httpUser' => '',
|
||||
'httpPass' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
|
@ -2090,8 +2084,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'type' => 'web',
|
||||
'name' => 'Web App',
|
||||
'key' => '',
|
||||
'store' => '',
|
||||
'hostname' => 'localhost',
|
||||
]);
|
||||
|
||||
|
|
@ -2112,8 +2104,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'type' => 'flutter-ios',
|
||||
'name' => 'Flutter App (iOS)',
|
||||
'key' => 'com.example.ios',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -2133,8 +2123,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'type' => 'flutter-android',
|
||||
'name' => 'Flutter App (Android)',
|
||||
'key' => 'com.example.android',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -2153,8 +2141,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'type' => 'flutter-web',
|
||||
'name' => 'Flutter App (Web)',
|
||||
'key' => '',
|
||||
'store' => '',
|
||||
'hostname' => 'flutter.appwrite.io',
|
||||
]);
|
||||
|
||||
|
|
@ -2175,8 +2161,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'type' => 'apple-ios',
|
||||
'name' => 'iOS App',
|
||||
'key' => 'com.example.ios',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -2196,8 +2180,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'type' => 'apple-macos',
|
||||
'name' => 'macOS App',
|
||||
'key' => 'com.example.macos',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -2217,8 +2199,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'type' => 'apple-watchos',
|
||||
'name' => 'watchOS App',
|
||||
'key' => 'com.example.watchos',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -2238,8 +2218,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'type' => 'apple-tvos',
|
||||
'name' => 'tvOS App',
|
||||
'key' => 'com.example.tvos',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
|
@ -2261,8 +2239,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'type' => 'unknown',
|
||||
'name' => 'Web App',
|
||||
'key' => '',
|
||||
'store' => '',
|
||||
'hostname' => 'localhost',
|
||||
]);
|
||||
|
||||
|
|
@ -2457,8 +2433,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Web App 2',
|
||||
'key' => '',
|
||||
'store' => '',
|
||||
'hostname' => 'localhost-new',
|
||||
]);
|
||||
|
||||
|
|
@ -2479,8 +2453,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'Flutter App (iOS) 2',
|
||||
'key' => 'com.example.ios2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -2500,8 +2472,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'Flutter App (Android) 2',
|
||||
'key' => 'com.example.android2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -2520,8 +2490,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'name' => 'Flutter App (Web) 2',
|
||||
'key' => '',
|
||||
'store' => '',
|
||||
'hostname' => 'flutter2.appwrite.io',
|
||||
]);
|
||||
|
||||
|
|
@ -2542,8 +2510,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'iOS App 2',
|
||||
'key' => 'com.example.ios2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -2563,8 +2529,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'macOS App 2',
|
||||
'key' => 'com.example.macos2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -2584,8 +2548,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'watchOS App 2',
|
||||
'key' => 'com.example.watchos2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -2605,8 +2567,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'tvOS App 2',
|
||||
'key' => 'com.example.tvos2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
|
|
@ -2627,8 +2587,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'name' => 'Flutter App (Android) 2',
|
||||
'key' => 'com.example.android2',
|
||||
'store' => '',
|
||||
'hostname' => '',
|
||||
]);
|
||||
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
|
|
@ -2836,8 +2794,6 @@ class ProjectsConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'type' => 'web',
|
||||
'name' => 'Too Long Hostname',
|
||||
'key' => '',
|
||||
'store' => '',
|
||||
'hostname' => \str_repeat("bestdomain", 25) . '.com' // 250 + 4 chars total (exactly above limit)
|
||||
]);
|
||||
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ class RealtimeConsoleClientTest extends Scope
|
|||
'users.*.delete',
|
||||
],
|
||||
'schedule' => '0 0 1 1 *',
|
||||
'timeout' => 10,
|
||||
'timeout' => 10
|
||||
]);
|
||||
|
||||
$functionId = $response1['body']['$id'] ?? '';
|
||||
|
|
@ -482,6 +482,7 @@ class RealtimeConsoleClientTest extends Scope
|
|||
], $this->getHeaders()), [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', \basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
|
|||
|
|
@ -1284,7 +1284,8 @@ class RealtimeCustomClientTest extends Scope
|
|||
'x-appwrite-key' => $this->getProject()['apiKey']
|
||||
]), [
|
||||
'entrypoint' => 'index.php',
|
||||
'code' => new CURLFile($code, 'application/x-gzip', basename($code))
|
||||
'code' => new CURLFile($code, 'application/x-gzip', basename($code)),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'] ?? '';
|
||||
|
|
@ -1458,6 +1459,43 @@ class RealtimeCustomClientTest extends Scope
|
|||
$this->assertContains("teams.*", $response['data']['events']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
|
||||
/**
|
||||
* Test Team Update Prefs
|
||||
*/
|
||||
$team = $this->client->call(Client::METHOD_PUT, '/teams/' . $teamId . '/prefs', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), [
|
||||
'prefs' => [
|
||||
'funcKey1' => 'funcValue1',
|
||||
'funcKey2' => 'funcValue2',
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals($team['headers']['status-code'], 200);
|
||||
$this->assertEquals($team['body']['funcKey1'], 'funcValue1');
|
||||
$this->assertEquals($team['body']['funcKey2'], 'funcValue2');
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
|
||||
$this->assertArrayHasKey('type', $response);
|
||||
$this->assertArrayHasKey('data', $response);
|
||||
$this->assertEquals('event', $response['type']);
|
||||
$this->assertNotEmpty($response['data']);
|
||||
$this->assertArrayHasKey('timestamp', $response['data']);
|
||||
$this->assertCount(2, $response['data']['channels']);
|
||||
$this->assertContains('teams', $response['data']['channels']);
|
||||
$this->assertContains("teams.{$teamId}", $response['data']['channels']);
|
||||
$this->assertContains("teams.{$teamId}.update", $response['data']['events']);
|
||||
$this->assertContains("teams.{$teamId}.update.prefs", $response['data']['events']);
|
||||
$this->assertContains("teams.{$teamId}", $response['data']['events']);
|
||||
$this->assertContains("teams.*.update.prefs", $response['data']['events']);
|
||||
$this->assertContains("teams.*.update", $response['data']['events']);
|
||||
$this->assertContains("teams.*", $response['data']['events']);
|
||||
$this->assertNotEmpty($response['data']['payload']);
|
||||
$this->assertEquals($response['data']['payload']['funcKey1'], 'funcValue1');
|
||||
$this->assertEquals($response['data']['payload']['funcKey2'], 'funcValue2');
|
||||
|
||||
$client->close();
|
||||
|
||||
return ['teamId' => $teamId];
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use Tests\E2E\Client;
|
|||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
trait StorageBase
|
||||
{
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ use Tests\E2E\Scopes\SideClient;
|
|||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
class StorageCustomClientTest extends Scope
|
||||
{
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use Tests\E2E\Scopes\ProjectCustom;
|
|||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Validator\DatetimeValidator;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
|
||||
class StorageCustomServerTest extends Scope
|
||||
{
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue